1 /* Sky Tentacles, Copyright (c) 2008-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 free_tentacles 0
18 # define release_tentacles 0
20 #define countof(x) (sizeof((x))/sizeof((*x)))
22 #include "xlockmore.h"
26 #include "gltrackball.h"
29 #include "xpm-ximage.h"
30 #include "../images/scales.xpm"
32 static char *grey_texture[] = {
40 #ifdef USE_GL /* whole file */
43 # define HAVE_POLYGONMODE
46 #define DEF_SPEED "1.0"
47 #define DEF_SMOOTH "True"
48 #define DEF_TEXTURE "True"
49 #define DEF_CEL "False"
50 #define DEF_INTERSECT "False"
51 #define DEF_SLICES "16"
52 #define DEF_SEGMENTS "24"
53 #define DEF_WIGGLINESS "0.35"
54 #define DEF_FLEXIBILITY "0.35"
55 #define DEF_THICKNESS "1.0"
56 #define DEF_LENGTH "9.0"
57 #define DEF_COLOR "#305A30"
58 #define DEF_STRIPE "#451A30"
59 #define DEF_SUCKER "#453E30"
60 #define DEF_DEBUG "False"
62 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
65 GLfloat length; /* length of the segment coming out of this segment */
66 GLfloat th; /* vector tilt (on yz plane) from previous segment */
67 GLfloat phi; /* vector rotation (on xy plane) from previous segment */
68 GLfloat thickness; /* radius of tentacle at the bottom of this segment */
69 rotator *rot; /* motion modeller */
74 GLfloat x, y, z; /* position of the base */
77 GLfloat tentacle_color[4], stripe_color[4], sucker_color[4];
81 GLXContext *glx_context;
82 trackball_state *trackball;
88 GLfloat tentacle_color[4], stripe_color[4], sucker_color[4];
95 GLfloat line_thickness;
96 GLfloat outline_color[4];
103 } tentacles_configuration;
105 static tentacles_configuration *tcs = NULL;
108 static GLfloat arg_speed;
110 static int texture_p;
112 static int intersect_p;
113 static int arg_slices;
114 static int arg_segments;
115 static GLfloat arg_thickness;
116 static GLfloat arg_length;
117 static GLfloat arg_wiggliness;
118 static GLfloat arg_flexibility;
119 static char *arg_color, *arg_stripe, *arg_sucker;
121 /* we can only have one light when doing cel shading */
122 static GLfloat light_pos[4] = {1.0, 1.0, 1.0, 0.0};
125 static XrmOptionDescRec opts[] = {
126 { "-speed", ".speed", XrmoptionSepArg, 0 },
127 { "-no-smooth", ".smooth", XrmoptionNoArg, "False" },
128 { "-texture", ".texture", XrmoptionNoArg, "True" },
129 { "-no-texture", ".texture", XrmoptionNoArg, "False" },
130 { "-cel", ".cel", XrmoptionNoArg, "True" },
131 { "-no-cel", ".cel", XrmoptionNoArg, "False" },
132 { "-intersect", ".intersect", XrmoptionNoArg, "True" },
133 { "-no-intersect", ".intersect", XrmoptionNoArg, "False" },
134 { "-slices", ".slices", XrmoptionSepArg, 0 },
135 { "-segments", ".segments", XrmoptionSepArg, 0 },
136 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
137 { "-length", ".length", XrmoptionSepArg, 0 },
138 { "-wiggliness", ".wiggliness", XrmoptionSepArg, 0 },
139 { "-flexibility", ".flexibility", XrmoptionSepArg, 0 },
140 { "-color", ".tentacleColor", XrmoptionSepArg, 0 },
141 { "-stripe-color", ".stripeColor", XrmoptionSepArg, 0 },
142 { "-sucker-color", ".suckerColor", XrmoptionSepArg, 0 },
143 { "-debug", ".debug", XrmoptionNoArg, "True" },
146 static argtype vars[] = {
147 {&arg_speed, "speed", "Speed", DEF_SPEED, t_Float},
148 {&smooth_p, "smooth", "Smooth", DEF_SMOOTH, t_Bool},
149 {&texture_p, "texture", "Texture", DEF_TEXTURE, t_Bool},
150 {&cel_p, "cel", "Cel", DEF_CEL, t_Bool},
151 {&intersect_p, "intersect", "Intersect", DEF_INTERSECT, t_Bool},
152 {&arg_slices, "slices", "Slices", DEF_SLICES, t_Int},
153 {&arg_segments, "segments", "Segments", DEF_SEGMENTS, t_Int},
154 {&arg_thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
155 {&arg_length, "length", "Length", DEF_LENGTH, t_Float},
156 {&arg_wiggliness, "wiggliness", "Wiggliness", DEF_WIGGLINESS, t_Float},
157 {&arg_flexibility, "flexibility", "Flexibility", DEF_FLEXIBILITY, t_Float},
158 {&arg_color, "tentacleColor", "Color", DEF_COLOR, t_String},
159 {&arg_stripe, "stripeColor", "Color", DEF_STRIPE, t_String},
160 {&arg_sucker, "suckerColor", "Color", DEF_SUCKER, t_String},
161 {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
164 ENTRYPOINT ModeSpecOpt tentacles_opts = {countof(opts), opts, countof(vars), vars, NULL};
167 /* Window management, etc
170 reshape_tentacles (ModeInfo *mi, int width, int height)
172 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
173 GLfloat h = (GLfloat) height / (GLfloat) width;
176 if (width > height * 5) { /* tiny window: show middle */
177 height = width * 9/16;
179 h = height / (GLfloat) width;
182 glViewport (0, y, (GLint) width, (GLint) height);
184 glMatrixMode(GL_PROJECTION);
186 gluPerspective (30.0, 1/h, 1.0, 100.0);
188 glMatrixMode(GL_MODELVIEW);
190 gluLookAt( 0.0, 0.0, 30.0,
194 glClear(GL_COLOR_BUFFER_BIT);
196 tc->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (3, width / 200));
201 normalize (GLfloat *x, GLfloat *y, GLfloat *z)
203 GLfloat d = sqrt((*x)*(*x) + (*y)*(*y) + (*z)*(*z));
210 dot (GLfloat x0, GLfloat y0, GLfloat z0,
211 GLfloat x1, GLfloat y1, GLfloat z1)
213 return x0*x1 + y0*y1 + z0*z1;
218 compute_unit_torus (ModeInfo *mi, double ratio, int slices1, int slices2)
220 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
221 Bool wire = MI_IS_WIREFRAME (mi);
224 if (wire) slices1 /= 2;
225 if (wire) slices2 /= 4;
226 if (slices1 < 3) slices1 = 3;
227 if (slices2 < 3) slices2 = 3;
229 tc->torus_polys = slices1 * (slices2+1) * 2;
230 tc->torus_points = (XYZ *) calloc (tc->torus_polys + 1,
231 sizeof (*tc->torus_points));
232 tc->torus_normals = (XYZ *) calloc (tc->torus_polys + 1,
233 sizeof (*tc->torus_normals));
234 tc->torus_step = 2 * (slices2+1);
236 for (i = 0; i < slices1; i++)
237 for (j = 0; j <= slices2; j++)
238 for (k = 0; k <= 1; k++)
240 double s = (i + k) % slices1 + 0.5;
241 double t = j % slices2;
243 p.x = cos(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
244 p.y = sin(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
245 p.z = sin(s*M_PI*2/slices1);
246 tc->torus_normals[fp] = p;
248 p.x = (1 + ratio * cos(s*M_PI*2/slices1)) * cos(t*M_PI*2/slices2) / 2;
249 p.y = (1 + ratio * cos(s*M_PI*2/slices1)) * sin(t*M_PI*2/slices2) / 2;
250 p.z = ratio * sin(s*M_PI*2/slices1) / 2;
251 tc->torus_points[fp] = p;
254 if (fp != tc->torus_polys) abort();
255 tc->torus_polys = fp;
259 /* Initializes a new tentacle and stores it in the list.
262 make_tentacle (ModeInfo *mi, int which, int total)
264 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
265 tentacle *t = (tentacle *) calloc (1, sizeof (*t));
271 /* position tentacles on a grid */
273 int cols = (int) (sqrt(total) + 0.5);
274 int rows = (total+cols-1) / cols;
275 int xx = which % cols;
276 int yy = which / cols;
277 double spc = arg_thickness * 0.8;
278 if (!intersect_p) cols = 1, xx = 0;
279 t->x = (cols * spc / 2) - (spc * (xx + 0.5));
280 t->y = (rows * spc / 2) - (spc * (yy + 0.5));
284 /* Brighten or darken the colors of this tentacle from the default.
286 brightness = 0.6 + frand(3.0);
287 memcpy (t->tentacle_color, tc->tentacle_color, 4 * sizeof(*t->tentacle_color));
288 memcpy (t->stripe_color, tc->stripe_color, 4 * sizeof(*t->stripe_color));
289 memcpy (t->sucker_color, tc->sucker_color, 4 * sizeof(*t->sucker_color));
291 t->X[0] *= brightness; if (t->X[0] > 1) t->X[0] = 1; \
292 t->X[1] *= brightness; if (t->X[1] > 1) t->X[1] = 1; \
293 t->X[2] *= brightness; if (t->X[2] > 1) t->X[2] = 1
294 FROB (tentacle_color);
299 t->nsegments = (arg_segments) + BELLRAND(arg_segments);
301 t->segments = (segment *) calloc (t->nsegments+1, sizeof(*t->segments));
302 for (i = 0; i < t->nsegments; i++)
304 double spin_speed = 0;
305 double spin_accel = 0;
306 double wander_speed = arg_speed * (0.02 + BELLRAND(0.1));
307 t->segments[i].rot = make_rotator (spin_speed, spin_speed, spin_speed,
308 spin_accel, wander_speed, True);
311 t->segments[0].thickness = (((arg_thickness * 0.5) +
312 BELLRAND(arg_thickness * 0.6))
315 if (tc->tentacles_size <= tc->ntentacles)
317 tc->tentacles_size = (tc->tentacles_size * 1.2) + tc->ntentacles + 2;
318 tc->tentacles = (tentacle **)
319 realloc (tc->tentacles, tc->tentacles_size * sizeof(*tc->tentacles));
322 fprintf (stderr, "%s: out of memory (%d tentacles)\n",
323 progname, tc->tentacles_size);
328 tc->tentacles[tc->ntentacles++] = t;
334 draw_sucker (tentacle *t, Bool front_p)
336 tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)];
337 Bool wire = MI_IS_WIREFRAME (t->mi);
339 int strips = tc->torus_polys / tc->torus_step;
342 glFrontFace (front_p ? GL_CW : GL_CCW);
343 for (i = 0; i < strips; i++)
345 int ii = i * tc->torus_step;
347 /* Leave off the polygons on the underside. This reduces polygon
348 count by about 10% with the default settings. */
349 if (strips > 4 && i >= strips/2 && i < strips-1)
352 glBegin (wire ? GL_LINE_STRIP : GL_QUAD_STRIP);
353 for (j = 0; j < tc->torus_step; j++)
355 XYZ sp = tc->torus_points[ii+j];
356 XYZ sn = tc->torus_normals[ii+j];
357 glNormal3f(sn.x, sn.y, sn.z);
358 glVertex3f(sp.x, sp.y, sp.z);
363 t->mi->polygon_count += points/2;
367 draw_tentacle_1 (tentacle *t, Bool front_p, Bool outline_p)
369 tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)];
371 Bool wire = MI_IS_WIREFRAME (t->mi);
372 XYZ ctr = { 0,0,0 }; /* current position of base of segment */
373 double cth = 0; /* overall orientation of current segment */
375 double cth_cos = 1, cth_sin = 0;
376 double cphi_cos = 1, cphi_sin = 0;
378 GLfloat light[3]; /* vector to the light */
380 GLfloat t0 = 0.0; /* texture coordinate */
382 XYZ *ring, *oring; /* points around the edge (this, and previous) */
383 XYZ *norm, *onorm; /* their normals */
384 XYZ *ucirc; /* unit circle, to save some trig */
386 /* Which portion of the radius the indented/colored stripe takes up */
387 int indented_points = arg_slices * 0.2;
389 /* We do rotation this way to minimize the number of calls to sin/cos.
390 We have to hack the transformations manually instead of using
391 glRotate/glTranslate because those calls are not allowed *inside*
392 of a glBegin/glEnd block...
394 # define ROT(P) do { \
396 _p.y = ((P.y * cth_sin - P.x * cth_cos)); \
397 _p.x = ((P.y * cth_cos + P.x * cth_sin) * cphi_sin - (P.z * cphi_cos)); \
398 _p.z = ((P.y * cth_cos + P.x * cth_sin) * cphi_cos + (P.z * cphi_sin)); \
402 ring = (XYZ *) malloc (arg_slices * sizeof(*ring));
403 norm = (XYZ *) malloc (arg_slices * sizeof(*norm));
404 oring = (XYZ *) malloc (arg_slices * sizeof(*oring));
405 onorm = (XYZ *) malloc (arg_slices * sizeof(*onorm));
406 ucirc = (XYZ *) malloc (arg_slices * sizeof(*ucirc));
408 light[0] = light_pos[0];
409 light[1] = light_pos[1];
410 light[2] = light_pos[2];
411 normalize (&light[0], &light[1], &light[2]);
413 for (i = 0; i < arg_slices; i++)
415 double a = M_PI * 2 * i / arg_slices;
422 # ifdef HAVE_POLYGONMODE
424 glPolygonMode (GL_FRONT_AND_BACK, (front_p ? GL_FILL : GL_LINE));
428 glTranslatef (t->x, t->y, t->z);
432 glPushAttrib (GL_ENABLE_BIT);
433 glDisable (GL_LIGHTING);
434 glDisable (GL_TEXTURE_1D);
435 glDisable (GL_TEXTURE_2D);
438 glBegin(GL_LINE_LOOP);
439 for (i = 0; i < arg_slices; i++)
440 glVertex3f (arg_thickness / 2 * cos (M_PI * 2 * i / arg_slices),
441 arg_thickness / 2 * sin (M_PI * 2 * i / arg_slices),
447 if (!front_p || outline_p)
448 glColor4fv (tc->outline_color);
450 glColor4fv (t->tentacle_color);
453 static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
454 static const GLfloat bshiny = 128.0;
455 glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
456 glMateriali (GL_FRONT, GL_SHININESS, bshiny);
459 for (i = 0; i < t->nsegments; i++)
462 GLfloat t1 = t0 + i / (t->nsegments * M_PI * 2);
464 for (j = 0; j < arg_slices; j++)
466 /* Construct a vertical disc at the origin, to use as the
467 base of this segment.
469 double r = t->segments[i].thickness / 2;
471 if (j <= indented_points/2 || j >= arg_slices-indented_points/2)
472 r *= 0.75; /* indent the stripe */
477 ring[j].x = r * ucirc[j].x;
479 ring[j].z = r * ucirc[j].y;
481 /* Then rotate the points by the angle of the current segment. */
484 /* Then move the ring to the base of this segment. */
491 /* Compute the normals of the faces on this segment. We do this
492 first so that the normals of the vertexes can be the average
493 of the normals of the faces.
494 #### Uh, except I didn't actually implement that...
495 but it would be a good idea.
498 for (j = 0; j <= arg_slices; j++)
500 int j0 = j % arg_slices;
501 int j1 = (j+1) % arg_slices;
502 norm[j0] = calc_normal (oring[j0], ring[j0], ring[j1]);
510 glLineWidth (tc->line_thickness);
511 glFrontFace (front_p ? GL_CCW : GL_CW);
512 glBegin (wire ? GL_LINES : smooth_p ? GL_QUAD_STRIP : GL_QUADS);
513 for (j = 0; j <= arg_slices; j++)
515 int j0 = j % arg_slices;
516 int j1 = (j+1) % arg_slices;
518 GLfloat ts = j / (double) arg_slices;
520 if (!front_p || outline_p)
521 glColor4fv (tc->outline_color);
522 else if (j <= indented_points/2 ||
523 j >= arg_slices-indented_points/2)
525 glColor4fv (t->stripe_color);
526 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
531 glColor4fv (t->tentacle_color);
532 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
536 /* For cel shading, the 1d texture coordinate (s) is the
537 dot product of the lighting vector and the vertex normal.
541 t0 = dot (light[0], light[1], light[2],
542 onorm[j0].x, onorm[j0].y, onorm[j0].z);
543 t1 = dot (light[0], light[1], light[2],
544 norm[j0].x, norm[j0].y, norm[j0].z);
549 glTexCoord2f (t0, ts);
550 glNormal3f (onorm[j0].x, onorm[j0].y, onorm[j0].z);
551 glVertex3f (oring[j0].x, oring[j0].y, oring[j0].z);
553 glTexCoord2f (t1, ts);
554 glNormal3f ( norm[j0].x, norm[j0].y, norm[j0].z);
555 glVertex3f ( ring[j0].x, ring[j0].y, ring[j0].z);
559 ts = j1 / (double) arg_slices;
560 glTexCoord2f (t1, ts);
561 glVertex3f ( ring[j1].x, ring[j1].y, ring[j1].z);
562 glTexCoord2f (t0, ts);
563 glVertex3f (oring[j1].x, oring[j1].y, oring[j1].z);
565 t->mi->polygon_count++;
571 glBegin (GL_LINE_LOOP);
572 for (j = 0; j < arg_slices; j++)
573 glVertex3f (ring[j].x, ring[j].y, ring[j].z);
577 /* Now draw the suckers!
580 double seg_length = arg_length / t->nsegments;
581 double sucker_size = arg_thickness / 5;
582 double sucker_spacing = sucker_size * 1.3;
583 int nsuckers = seg_length / sucker_spacing;
584 double oth = cth - t->segments[i-1].th;
585 double ophi = cphi - t->segments[i-1].phi;
589 glLineWidth (MAX (2, tc->line_thickness / 2.0));
590 glDisable (GL_TEXTURE_2D);
592 /* Sometimes we have N suckers on one segment;
593 sometimes we have one sucker every N segments. */
596 int segs_per_sucker =
597 (int) ((sucker_spacing / seg_length) + 0.5);
598 nsuckers = (i % segs_per_sucker) ? 0 : 1;
603 glColor4fv (tc->outline_color);
604 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
609 glColor4fv (t->sucker_color);
610 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
614 for (k = 0; k < nsuckers; k++)
620 p.x = p0.x + (p1.x - p0.x) * (k + 0.5) / nsuckers;
621 p.y = p0.y + (p1.y - p0.y) * (k + 0.5) / nsuckers;
622 p.z = p0.z + (p1.z - p0.z) * (k + 0.5) / nsuckers;
625 glTranslatef (p.x, p.y, p.z);
626 glRotatef (ophi * 180 / M_PI, 0, 1, 0);
627 glRotatef (-oth * 180 / M_PI, 1, 0, 0);
628 glRotatef (90, 1, 0, 0);
630 { /* Not quite right: this is the slope of the outer edge
631 if the next segment was not tilted at all... If there
632 is any tilt, then the angle of this wall and the
633 opposite wall are very different.
635 double slope = ((t->segments[i-1].thickness -
636 t->segments[i].thickness) /
637 t->segments[i].length);
638 glRotatef (-45 * slope, 1, 0, 0);
641 scale = t->segments[i].thickness / arg_thickness;
642 scale *= 0.7 * sucker_size;
644 glScalef (scale, scale, scale * 4);
646 glTranslatef (0, 0, -0.1); /* embed */
651 glScalef (scale, scale, scale);
654 glTranslatef (1, 0, 0); /* left */
655 draw_sucker (t, front_p);
657 glTranslatef (-2, 0, 0); /* right */
658 draw_sucker (t, front_p);
663 if (texture_p) glEnable (GL_TEXTURE_2D);
667 /* Now draw the end caps.
669 glLineWidth (tc->line_thickness);
670 if (!outline_p && (i == 0 || i == t->nsegments-1))
673 GLfloat ctrz = ctr.z + ((i == 0 ? -1 : 1) *
674 t->segments[i].thickness / 4);
677 glColor4fv (t->tentacle_color);
678 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
681 glFrontFace ((front_p ? i == 0 : i != 0) ? GL_CCW : GL_CW);
682 glBegin (wire ? GL_LINES : GL_TRIANGLE_FAN);
683 glNormal3f (0, 0, (i == 0 ? -1 : 1));
684 glTexCoord2f (t0 - 0.25, 0.5);
685 glVertex3f (ctr.x, ctr.y, ctrz);
686 for (j = 0; j <= arg_slices; j++)
688 int jj = j % arg_slices;
689 GLfloat ts = j / (double) arg_slices;
690 glTexCoord2f (t0, ts);
691 glNormal3f (norm[jj].x, norm[jj].y, norm[jj].z);
692 glVertex3f (ring[jj].x, ring[jj].y, ring[jj].z);
693 if (wire) glVertex3f (ctr.x, ctr.y, ctrz);
694 t->mi->polygon_count++;
699 /* Now move to the end of this segment in preparation for the next.
701 if (i != t->nsegments-1)
705 p.y = t->segments[i].length;
712 /* Accumulate the current angle and rotation, to keep track of the
713 rotation of the upcoming segment.
715 cth += t->segments[i].th;
716 cphi += t->segments[i].phi;
720 cphi_sin = sin (cphi);
721 cphi_cos = cos (cphi);
723 memcpy (oring, ring, arg_slices * sizeof(*ring));
724 memcpy (onorm, norm, arg_slices * sizeof(*norm));
741 draw_tentacle (tentacle *t, Bool front_p)
743 # ifndef HAVE_POLYGONMODE
744 Bool wire = MI_IS_WIREFRAME (t->mi);
745 if (!wire && cel_p && front_p)
747 draw_tentacle_1 (t, front_p, True);
748 glClear (GL_DEPTH_BUFFER_BIT);
750 # endif /* HAVE_POLYGONMODE */
752 draw_tentacle_1 (t, front_p, False);
757 move_tentacle (tentacle *t)
759 /* tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)]; */
763 int skip = t->nsegments * (1 - (arg_wiggliness + 0.5));
767 for (i = 0; i < t->nsegments; i++)
769 if (++tick >= skip || i == t->nsegments-1)
771 /* randomize the motion of this segment... */
773 double phi_range = M_PI * 0.8 * arg_flexibility;
774 double th_range = M_PI * 0.9 * arg_flexibility;
775 get_position (t->segments[i].rot, &x, &y, &z, True);
776 t->segments[i].phi = phi_range * (0.5 - y);
777 t->segments[i].th = th_range * (0.5 - z);
778 t->segments[i].length = ((0.8 + ((0.5 - x) * 0.4)) *
779 (arg_length / t->nsegments));
781 /* ...and make the previous N segments be interpolated
782 between this one and the previous randomized one. */
783 for (j = last+1; j <= i; j++)
785 t->segments[j].phi = (t->segments[i].phi / (i - last));
786 t->segments[j].th = (t->segments[i].th / (i - last));
787 t->segments[j].length = (t->segments[i].length);
793 len += t->segments[i].length;
796 /* thickness of segment is relative to current position on tentacle
797 (not just the index of the segment). */
798 for (i = 0; i < t->nsegments; i++)
802 double tt = (t->segments[0].thickness * (len - pos) / len);
803 if (tt < 0.001) tt = 0.001;
804 t->segments[i].thickness = tt;
806 pos += t->segments[i].length;
813 tentacles_handle_event (ModeInfo *mi, XEvent *event)
815 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
817 if (gltrackball_event_handler (event, tc->trackball,
818 MI_WIDTH (mi), MI_HEIGHT (mi),
821 else if (event->xany.type == KeyPress)
825 XLookupString (&event->xkey, &c, 1, &keysym, 0);
828 gltrackball_reset (tc->trackball, 0, 0);
838 parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
841 a[3] = 1.0; /* alpha */
843 if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
845 fprintf (stderr, "%s: can't parse %s color %s", progname, name, s);
848 a[0] = c.red / 65536.0;
849 a[1] = c.green / 65536.0;
850 a[2] = c.blue / 65536.0;
855 init_tentacles (ModeInfo *mi)
857 tentacles_configuration *tc;
858 int wire = MI_IS_WIREFRAME(mi);
863 tc = &tcs[MI_SCREEN(mi)];
865 tc->glx_context = init_GL(mi);
867 reshape_tentacles (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
871 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
872 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
873 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
874 glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
875 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
876 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
877 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
882 glEnable (GL_LIGHTING);
883 glEnable (GL_LIGHT0);
886 tc->trackball = gltrackball_init (False);
888 tc->left_p = !(random() % 5);
890 if (arg_segments < 2) arg_segments = 2;
891 if (arg_slices < 3) arg_slices = 3;
892 if (arg_thickness < 0.1) arg_thickness = 0.1;
893 if (arg_wiggliness < 0) arg_wiggliness = 0;
894 if (arg_wiggliness > 1) arg_wiggliness = 1;
895 if (arg_flexibility < 0) arg_flexibility = 0;
896 if (arg_flexibility > 1) arg_flexibility = 1;
898 parse_color (mi, "tentacleColor", arg_color, tc->tentacle_color);
899 parse_color (mi, "stripeColor", arg_stripe, tc->stripe_color);
900 parse_color (mi, "suckerColor", arg_sucker, tc->sucker_color);
902 /* Black outlines for light colors, white outlines for dark colors. */
903 if (tc->tentacle_color[0] + tc->tentacle_color[1] + tc->tentacle_color[2]
905 tc->outline_color[0] = 1;
906 tc->outline_color[1] = tc->outline_color[0];
907 tc->outline_color[2] = tc->outline_color[0];
908 tc->outline_color[3] = 1;
910 for (i = 0; i < MI_COUNT(mi); i++)
911 move_tentacle (make_tentacle (mi, i, MI_COUNT(mi)));
913 if (wire) texture_p = cel_p = False;
914 if (cel_p) texture_p = False;
916 if (texture_p || cel_p) {
917 glGenTextures(1, &tc->texid);
918 # ifdef HAVE_GLBINDTEXTURE
919 glBindTexture ((cel_p ? GL_TEXTURE_1D : GL_TEXTURE_2D), tc->texid);
922 tc->texture = xpm_to_ximage (MI_DISPLAY(mi), MI_VISUAL(mi),
924 (cel_p ? grey_texture : scales));
925 if (!tc->texture) texture_p = cel_p = False;
929 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
931 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
932 tc->texture->width, tc->texture->height, 0,
934 /* GL_UNSIGNED_BYTE, */
935 GL_UNSIGNED_INT_8_8_8_8_REV,
937 check_gl_error("texture");
939 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
940 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
942 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
943 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
945 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
947 glEnable(GL_TEXTURE_2D);
950 glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA,
951 tc->texture->width, 0,
953 /* GL_UNSIGNED_BYTE, */
954 GL_UNSIGNED_INT_8_8_8_8_REV,
956 check_gl_error("texture");
958 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
959 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);
961 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
962 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
964 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
966 glEnable(GL_TEXTURE_1D);
967 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
968 glEnable (GL_LINE_SMOOTH);
969 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
972 /* Dark gray instead of black, so the outlines show up */
973 glClearColor (0.13, 0.13, 0.13, 1.0);
976 compute_unit_torus (mi, 0.5,
977 MAX(5, arg_slices/6),
978 MAX(9, arg_slices/3));
983 draw_tentacles (ModeInfo *mi)
985 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
986 Display *dpy = MI_DISPLAY(mi);
987 Window window = MI_WINDOW(mi);
990 if (!tc->glx_context)
993 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tc->glx_context));
995 glShadeModel(GL_SMOOTH);
997 glEnable(GL_DEPTH_TEST);
998 glEnable(GL_NORMALIZE);
999 glEnable(GL_CULL_FACE);
1001 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1004 glRotatef(current_device_rotation(), 0, 0, 1);
1009 glPushAttrib (GL_ENABLE_BIT);
1011 { GLfloat s = 8.7/1600; glScalef(s,s,s); }
1012 glTranslatef(-800,-514,0);
1013 glDisable(GL_LIGHTING);
1014 glDisable(GL_TEXTURE_1D);
1015 glDisable(GL_TEXTURE_2D);
1016 glColor3f (1, 1, 1);
1017 glBegin(GL_LINE_LOOP);
1019 glVertex3f(0,1028,0);
1020 glVertex3f(1600,1028,0);
1021 glVertex3f(1600,0,0);
1027 gltrackball_rotate (tc->trackball);
1029 mi->polygon_count = 0;
1033 glPushAttrib (GL_ENABLE_BIT);
1034 glDisable (GL_LIGHTING);
1035 glDisable (GL_TEXTURE_1D);
1036 glDisable (GL_TEXTURE_2D);
1037 glColor3f (1, 1, 1);
1040 glVertex3f(-0.5, 0, 0); glVertex3f(0.5, 0, 0);
1041 glVertex3f(0, -0.5, 0); glVertex3f(0, 0.5, 0);
1052 glRotatef (ry, 0, 1, 0);
1053 glRotatef (rx, 1, 0, 0);
1054 glRotatef (rz, 0, 0, 1);
1056 glTranslatef (0, -2.0, -4.5);
1058 glTranslatef (0, -2.5, -5.0);
1061 if (!tc->button_down_p)
1062 for (i = 0; i < tc->ntentacles; i++)
1063 move_tentacle (tc->tentacles[i]);
1066 for (i = 0; i < tc->ntentacles; i++)
1069 glClear(GL_DEPTH_BUFFER_BIT);
1070 draw_tentacle (tc->tentacles[i], True);
1072 draw_tentacle (tc->tentacles[i], False);
1078 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1079 draw_sucker (tc->tentacles[0], True);
1082 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1083 glLineWidth (tc->line_thickness);
1084 glColor4fv (tc->outline_color);
1085 draw_sucker (tc->tentacles[0], False);
1091 if (mi->fps_p) do_fps (mi);
1094 glXSwapBuffers(dpy, window);
1097 XSCREENSAVER_MODULE_2 ("SkyTentacles", skytentacles, tentacles)