1 /* Sky Tentacles, Copyright (c) 2008-2018 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 "ximage-loader.h"
30 #include "images/gen/scales_png.h"
32 #ifdef USE_GL /* whole file */
35 # define HAVE_POLYGONMODE
38 #define DEF_SPEED "1.0"
39 #define DEF_SMOOTH "True"
40 #define DEF_TEXTURE "True"
41 #define DEF_CEL "False"
42 #define DEF_INTERSECT "False"
43 #define DEF_SLICES "16"
44 #define DEF_SEGMENTS "24"
45 #define DEF_WIGGLINESS "0.35"
46 #define DEF_FLEXIBILITY "0.35"
47 #define DEF_THICKNESS "1.0"
48 #define DEF_LENGTH "9.0"
49 #define DEF_COLOR "#305A30"
50 #define DEF_STRIPE "#451A30"
51 #define DEF_SUCKER "#453E30"
52 #define DEF_DEBUG "False"
54 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
57 GLfloat length; /* length of the segment coming out of this segment */
58 GLfloat th; /* vector tilt (on yz plane) from previous segment */
59 GLfloat phi; /* vector rotation (on xy plane) from previous segment */
60 GLfloat thickness; /* radius of tentacle at the bottom of this segment */
61 rotator *rot; /* motion modeller */
66 GLfloat x, y, z; /* position of the base */
69 GLfloat tentacle_color[4], stripe_color[4], sucker_color[4];
73 GLXContext *glx_context;
74 trackball_state *trackball;
80 GLfloat tentacle_color[4], stripe_color[4], sucker_color[4];
87 GLfloat line_thickness;
88 GLfloat outline_color[4];
95 } tentacles_configuration;
97 static tentacles_configuration *tcs = NULL;
100 static GLfloat arg_speed;
102 static int texture_p;
104 static int intersect_p;
105 static int arg_slices;
106 static int arg_segments;
107 static GLfloat arg_thickness;
108 static GLfloat arg_length;
109 static GLfloat arg_wiggliness;
110 static GLfloat arg_flexibility;
111 static char *arg_color, *arg_stripe, *arg_sucker;
113 /* we can only have one light when doing cel shading */
114 static GLfloat light_pos[4] = {1.0, 1.0, 1.0, 0.0};
117 static XrmOptionDescRec opts[] = {
118 { "-speed", ".speed", XrmoptionSepArg, 0 },
119 { "-no-smooth", ".smooth", XrmoptionNoArg, "False" },
120 { "-texture", ".texture", XrmoptionNoArg, "True" },
121 { "-no-texture", ".texture", XrmoptionNoArg, "False" },
122 { "-cel", ".cel", XrmoptionNoArg, "True" },
123 { "-no-cel", ".cel", XrmoptionNoArg, "False" },
124 { "-intersect", ".intersect", XrmoptionNoArg, "True" },
125 { "-no-intersect", ".intersect", XrmoptionNoArg, "False" },
126 { "-slices", ".slices", XrmoptionSepArg, 0 },
127 { "-segments", ".segments", XrmoptionSepArg, 0 },
128 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
129 { "-length", ".length", XrmoptionSepArg, 0 },
130 { "-wiggliness", ".wiggliness", XrmoptionSepArg, 0 },
131 { "-flexibility", ".flexibility", XrmoptionSepArg, 0 },
132 { "-color", ".tentacleColor", XrmoptionSepArg, 0 },
133 { "-stripe-color", ".stripeColor", XrmoptionSepArg, 0 },
134 { "-sucker-color", ".suckerColor", XrmoptionSepArg, 0 },
135 { "-debug", ".debug", XrmoptionNoArg, "True" },
138 static argtype vars[] = {
139 {&arg_speed, "speed", "Speed", DEF_SPEED, t_Float},
140 {&smooth_p, "smooth", "Smooth", DEF_SMOOTH, t_Bool},
141 {&texture_p, "texture", "Texture", DEF_TEXTURE, t_Bool},
142 {&cel_p, "cel", "Cel", DEF_CEL, t_Bool},
143 {&intersect_p, "intersect", "Intersect", DEF_INTERSECT, t_Bool},
144 {&arg_slices, "slices", "Slices", DEF_SLICES, t_Int},
145 {&arg_segments, "segments", "Segments", DEF_SEGMENTS, t_Int},
146 {&arg_thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
147 {&arg_length, "length", "Length", DEF_LENGTH, t_Float},
148 {&arg_wiggliness, "wiggliness", "Wiggliness", DEF_WIGGLINESS, t_Float},
149 {&arg_flexibility, "flexibility", "Flexibility", DEF_FLEXIBILITY, t_Float},
150 {&arg_color, "tentacleColor", "Color", DEF_COLOR, t_String},
151 {&arg_stripe, "stripeColor", "Color", DEF_STRIPE, t_String},
152 {&arg_sucker, "suckerColor", "Color", DEF_SUCKER, t_String},
153 {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
156 ENTRYPOINT ModeSpecOpt tentacles_opts = {countof(opts), opts, countof(vars), vars, NULL};
159 /* Window management, etc
162 reshape_tentacles (ModeInfo *mi, int width, int height)
164 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
165 GLfloat h = (GLfloat) height / (GLfloat) width;
168 if (width > height * 5) { /* tiny window: show middle */
169 height = width * 9/16;
171 h = height / (GLfloat) width;
174 glViewport (0, y, (GLint) width, (GLint) height);
176 glMatrixMode(GL_PROJECTION);
178 gluPerspective (30.0, 1/h, 1.0, 100.0);
180 glMatrixMode(GL_MODELVIEW);
182 gluLookAt( 0.0, 0.0, 30.0,
186 glClear(GL_COLOR_BUFFER_BIT);
188 tc->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (3, width / 200));
193 normalize (GLfloat *x, GLfloat *y, GLfloat *z)
195 GLfloat d = sqrt((*x)*(*x) + (*y)*(*y) + (*z)*(*z));
202 dot (GLfloat x0, GLfloat y0, GLfloat z0,
203 GLfloat x1, GLfloat y1, GLfloat z1)
205 return x0*x1 + y0*y1 + z0*z1;
210 compute_unit_torus (ModeInfo *mi, double ratio, int slices1, int slices2)
212 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
213 Bool wire = MI_IS_WIREFRAME (mi);
216 if (wire) slices1 /= 2;
217 if (wire) slices2 /= 4;
218 if (slices1 < 3) slices1 = 3;
219 if (slices2 < 3) slices2 = 3;
221 tc->torus_polys = slices1 * (slices2+1) * 2;
222 tc->torus_points = (XYZ *) calloc (tc->torus_polys + 1,
223 sizeof (*tc->torus_points));
224 tc->torus_normals = (XYZ *) calloc (tc->torus_polys + 1,
225 sizeof (*tc->torus_normals));
226 tc->torus_step = 2 * (slices2+1);
228 for (i = 0; i < slices1; i++)
229 for (j = 0; j <= slices2; j++)
230 for (k = 0; k <= 1; k++)
232 double s = (i + k) % slices1 + 0.5;
233 double t = j % slices2;
235 p.x = cos(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
236 p.y = sin(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
237 p.z = sin(s*M_PI*2/slices1);
238 tc->torus_normals[fp] = p;
240 p.x = (1 + ratio * cos(s*M_PI*2/slices1)) * cos(t*M_PI*2/slices2) / 2;
241 p.y = (1 + ratio * cos(s*M_PI*2/slices1)) * sin(t*M_PI*2/slices2) / 2;
242 p.z = ratio * sin(s*M_PI*2/slices1) / 2;
243 tc->torus_points[fp] = p;
246 if (fp != tc->torus_polys) abort();
247 tc->torus_polys = fp;
251 /* Initializes a new tentacle and stores it in the list.
254 make_tentacle (ModeInfo *mi, int which, int total)
256 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
257 tentacle *t = (tentacle *) calloc (1, sizeof (*t));
263 /* position tentacles on a grid */
265 int cols = (int) (sqrt(total) + 0.5);
266 int rows = (total+cols-1) / cols;
267 int xx = which % cols;
268 int yy = which / cols;
269 double spc = arg_thickness * 0.8;
270 if (!intersect_p) cols = 1, xx = 0;
271 t->x = (cols * spc / 2) - (spc * (xx + 0.5));
272 t->y = (rows * spc / 2) - (spc * (yy + 0.5));
276 /* Brighten or darken the colors of this tentacle from the default.
278 brightness = 0.6 + frand(3.0);
279 memcpy (t->tentacle_color, tc->tentacle_color, 4 * sizeof(*t->tentacle_color));
280 memcpy (t->stripe_color, tc->stripe_color, 4 * sizeof(*t->stripe_color));
281 memcpy (t->sucker_color, tc->sucker_color, 4 * sizeof(*t->sucker_color));
283 t->X[0] *= brightness; if (t->X[0] > 1) t->X[0] = 1; \
284 t->X[1] *= brightness; if (t->X[1] > 1) t->X[1] = 1; \
285 t->X[2] *= brightness; if (t->X[2] > 1) t->X[2] = 1
286 FROB (tentacle_color);
291 t->nsegments = (arg_segments) + BELLRAND(arg_segments);
293 t->segments = (segment *) calloc (t->nsegments+1, sizeof(*t->segments));
294 for (i = 0; i < t->nsegments; i++)
296 double spin_speed = 0;
297 double spin_accel = 0;
298 double wander_speed = arg_speed * (0.02 + BELLRAND(0.1));
299 t->segments[i].rot = make_rotator (spin_speed, spin_speed, spin_speed,
300 spin_accel, wander_speed, True);
303 t->segments[0].thickness = (((arg_thickness * 0.5) +
304 BELLRAND(arg_thickness * 0.6))
307 if (tc->tentacles_size <= tc->ntentacles)
309 tc->tentacles_size = (tc->tentacles_size * 1.2) + tc->ntentacles + 2;
310 tc->tentacles = (tentacle **)
311 realloc (tc->tentacles, tc->tentacles_size * sizeof(*tc->tentacles));
314 fprintf (stderr, "%s: out of memory (%d tentacles)\n",
315 progname, tc->tentacles_size);
320 tc->tentacles[tc->ntentacles++] = t;
326 draw_sucker (tentacle *t, Bool front_p)
328 tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)];
329 Bool wire = MI_IS_WIREFRAME (t->mi);
331 int strips = tc->torus_polys / tc->torus_step;
334 glFrontFace (front_p ? GL_CW : GL_CCW);
335 for (i = 0; i < strips; i++)
337 int ii = i * tc->torus_step;
339 /* Leave off the polygons on the underside. This reduces polygon
340 count by about 10% with the default settings. */
341 if (strips > 4 && i >= strips/2 && i < strips-1)
344 glBegin (wire ? GL_LINE_STRIP : GL_QUAD_STRIP);
345 for (j = 0; j < tc->torus_step; j++)
347 XYZ sp = tc->torus_points[ii+j];
348 XYZ sn = tc->torus_normals[ii+j];
349 glNormal3f(sn.x, sn.y, sn.z);
350 glVertex3f(sp.x, sp.y, sp.z);
355 t->mi->polygon_count += points/2;
359 draw_tentacle_1 (tentacle *t, Bool front_p, Bool outline_p)
361 tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)];
363 Bool wire = MI_IS_WIREFRAME (t->mi);
364 XYZ ctr = { 0,0,0 }; /* current position of base of segment */
365 double cth = 0; /* overall orientation of current segment */
367 double cth_cos = 1, cth_sin = 0;
368 double cphi_cos = 1, cphi_sin = 0;
370 GLfloat light[3]; /* vector to the light */
372 GLfloat t0 = 0.0; /* texture coordinate */
374 XYZ *ring, *oring; /* points around the edge (this, and previous) */
375 XYZ *norm, *onorm; /* their normals */
376 XYZ *ucirc; /* unit circle, to save some trig */
378 /* Which portion of the radius the indented/colored stripe takes up */
379 int indented_points = arg_slices * 0.2;
381 /* We do rotation this way to minimize the number of calls to sin/cos.
382 We have to hack the transformations manually instead of using
383 glRotate/glTranslate because those calls are not allowed *inside*
384 of a glBegin/glEnd block...
386 # define ROT(P) do { \
388 _p.y = ((P.y * cth_sin - P.x * cth_cos)); \
389 _p.x = ((P.y * cth_cos + P.x * cth_sin) * cphi_sin - (P.z * cphi_cos)); \
390 _p.z = ((P.y * cth_cos + P.x * cth_sin) * cphi_cos + (P.z * cphi_sin)); \
394 ring = (XYZ *) malloc (arg_slices * sizeof(*ring));
395 norm = (XYZ *) malloc (arg_slices * sizeof(*norm));
396 oring = (XYZ *) malloc (arg_slices * sizeof(*oring));
397 onorm = (XYZ *) malloc (arg_slices * sizeof(*onorm));
398 ucirc = (XYZ *) malloc (arg_slices * sizeof(*ucirc));
400 light[0] = light_pos[0];
401 light[1] = light_pos[1];
402 light[2] = light_pos[2];
403 normalize (&light[0], &light[1], &light[2]);
405 for (i = 0; i < arg_slices; i++)
407 double a = M_PI * 2 * i / arg_slices;
414 # ifdef HAVE_POLYGONMODE
416 glPolygonMode (GL_FRONT_AND_BACK, (front_p ? GL_FILL : GL_LINE));
420 glTranslatef (t->x, t->y, t->z);
424 glPushAttrib (GL_ENABLE_BIT);
425 glDisable (GL_LIGHTING);
426 glDisable (GL_TEXTURE_1D);
427 glDisable (GL_TEXTURE_2D);
430 glBegin(GL_LINE_LOOP);
431 for (i = 0; i < arg_slices; i++)
432 glVertex3f (arg_thickness / 2 * cos (M_PI * 2 * i / arg_slices),
433 arg_thickness / 2 * sin (M_PI * 2 * i / arg_slices),
439 if (!front_p || outline_p)
440 glColor4fv (tc->outline_color);
442 glColor4fv (t->tentacle_color);
445 static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
446 static const GLfloat bshiny = 128.0;
447 glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
448 glMateriali (GL_FRONT, GL_SHININESS, bshiny);
451 for (i = 0; i < t->nsegments; i++)
454 GLfloat t1 = t0 + i / (t->nsegments * M_PI * 2);
456 for (j = 0; j < arg_slices; j++)
458 /* Construct a vertical disc at the origin, to use as the
459 base of this segment.
461 double r = t->segments[i].thickness / 2;
463 if (j <= indented_points/2 || j >= arg_slices-indented_points/2)
464 r *= 0.75; /* indent the stripe */
469 ring[j].x = r * ucirc[j].x;
471 ring[j].z = r * ucirc[j].y;
473 /* Then rotate the points by the angle of the current segment. */
476 /* Then move the ring to the base of this segment. */
483 /* Compute the normals of the faces on this segment. We do this
484 first so that the normals of the vertexes can be the average
485 of the normals of the faces.
486 #### Uh, except I didn't actually implement that...
487 but it would be a good idea.
490 for (j = 0; j <= arg_slices; j++)
492 int j0 = j % arg_slices;
493 int j1 = (j+1) % arg_slices;
494 norm[j0] = calc_normal (oring[j0], ring[j0], ring[j1]);
502 glLineWidth (tc->line_thickness);
503 glFrontFace (front_p ? GL_CCW : GL_CW);
504 glBegin (wire ? GL_LINES : smooth_p ? GL_QUAD_STRIP : GL_QUADS);
505 for (j = 0; j <= arg_slices; j++)
507 int j0 = j % arg_slices;
508 int j1 = (j+1) % arg_slices;
510 GLfloat ts = j / (double) arg_slices;
512 if (!front_p || outline_p)
513 glColor4fv (tc->outline_color);
514 else if (j <= indented_points/2 ||
515 j >= arg_slices-indented_points/2)
517 glColor4fv (t->stripe_color);
518 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
523 glColor4fv (t->tentacle_color);
524 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
528 /* For cel shading, the 1d texture coordinate (s) is the
529 dot product of the lighting vector and the vertex normal.
533 t0 = dot (light[0], light[1], light[2],
534 onorm[j0].x, onorm[j0].y, onorm[j0].z);
535 t1 = dot (light[0], light[1], light[2],
536 norm[j0].x, norm[j0].y, norm[j0].z);
541 glTexCoord2f (t0, ts);
542 glNormal3f (onorm[j0].x, onorm[j0].y, onorm[j0].z);
543 glVertex3f (oring[j0].x, oring[j0].y, oring[j0].z);
545 glTexCoord2f (t1, ts);
546 glNormal3f ( norm[j0].x, norm[j0].y, norm[j0].z);
547 glVertex3f ( ring[j0].x, ring[j0].y, ring[j0].z);
551 ts = j1 / (double) arg_slices;
552 glTexCoord2f (t1, ts);
553 glVertex3f ( ring[j1].x, ring[j1].y, ring[j1].z);
554 glTexCoord2f (t0, ts);
555 glVertex3f (oring[j1].x, oring[j1].y, oring[j1].z);
557 t->mi->polygon_count++;
563 glBegin (GL_LINE_LOOP);
564 for (j = 0; j < arg_slices; j++)
565 glVertex3f (ring[j].x, ring[j].y, ring[j].z);
569 /* Now draw the suckers!
572 double seg_length = arg_length / t->nsegments;
573 double sucker_size = arg_thickness / 5;
574 double sucker_spacing = sucker_size * 1.3;
575 int nsuckers = seg_length / sucker_spacing;
576 double oth = cth - t->segments[i-1].th;
577 double ophi = cphi - t->segments[i-1].phi;
581 glLineWidth (MAX (2, tc->line_thickness / 2.0));
582 glDisable (GL_TEXTURE_2D);
584 /* Sometimes we have N suckers on one segment;
585 sometimes we have one sucker every N segments. */
588 int segs_per_sucker =
589 (int) ((sucker_spacing / seg_length) + 0.5);
590 nsuckers = (i % segs_per_sucker) ? 0 : 1;
595 glColor4fv (tc->outline_color);
596 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
601 glColor4fv (t->sucker_color);
602 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
606 for (k = 0; k < nsuckers; k++)
612 p.x = p0.x + (p1.x - p0.x) * (k + 0.5) / nsuckers;
613 p.y = p0.y + (p1.y - p0.y) * (k + 0.5) / nsuckers;
614 p.z = p0.z + (p1.z - p0.z) * (k + 0.5) / nsuckers;
617 glTranslatef (p.x, p.y, p.z);
618 glRotatef (ophi * 180 / M_PI, 0, 1, 0);
619 glRotatef (-oth * 180 / M_PI, 1, 0, 0);
620 glRotatef (90, 1, 0, 0);
622 { /* Not quite right: this is the slope of the outer edge
623 if the next segment was not tilted at all... If there
624 is any tilt, then the angle of this wall and the
625 opposite wall are very different.
627 double slope = ((t->segments[i-1].thickness -
628 t->segments[i].thickness) /
629 t->segments[i].length);
630 glRotatef (-45 * slope, 1, 0, 0);
633 scale = t->segments[i].thickness / arg_thickness;
634 scale *= 0.7 * sucker_size;
636 glScalef (scale, scale, scale * 4);
638 glTranslatef (0, 0, -0.1); /* embed */
643 glScalef (scale, scale, scale);
646 glTranslatef (1, 0, 0); /* left */
647 draw_sucker (t, front_p);
649 glTranslatef (-2, 0, 0); /* right */
650 draw_sucker (t, front_p);
655 if (texture_p) glEnable (GL_TEXTURE_2D);
659 /* Now draw the end caps.
661 glLineWidth (tc->line_thickness);
662 if (!outline_p && (i == 0 || i == t->nsegments-1))
665 GLfloat ctrz = ctr.z + ((i == 0 ? -1 : 1) *
666 t->segments[i].thickness / 4);
669 glColor4fv (t->tentacle_color);
670 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
673 glFrontFace ((front_p ? i == 0 : i != 0) ? GL_CCW : GL_CW);
674 glBegin (wire ? GL_LINES : GL_TRIANGLE_FAN);
675 glNormal3f (0, 0, (i == 0 ? -1 : 1));
676 glTexCoord2f (t0 - 0.25, 0.5);
677 glVertex3f (ctr.x, ctr.y, ctrz);
678 for (j = 0; j <= arg_slices; j++)
680 int jj = j % arg_slices;
681 GLfloat ts = j / (double) arg_slices;
682 glTexCoord2f (t0, ts);
683 glNormal3f (norm[jj].x, norm[jj].y, norm[jj].z);
684 glVertex3f (ring[jj].x, ring[jj].y, ring[jj].z);
685 if (wire) glVertex3f (ctr.x, ctr.y, ctrz);
686 t->mi->polygon_count++;
691 /* Now move to the end of this segment in preparation for the next.
693 if (i != t->nsegments-1)
697 p.y = t->segments[i].length;
704 /* Accumulate the current angle and rotation, to keep track of the
705 rotation of the upcoming segment.
707 cth += t->segments[i].th;
708 cphi += t->segments[i].phi;
712 cphi_sin = sin (cphi);
713 cphi_cos = cos (cphi);
715 memcpy (oring, ring, arg_slices * sizeof(*ring));
716 memcpy (onorm, norm, arg_slices * sizeof(*norm));
733 draw_tentacle (tentacle *t, Bool front_p)
735 # ifndef HAVE_POLYGONMODE
736 Bool wire = MI_IS_WIREFRAME (t->mi);
737 if (!wire && cel_p && front_p)
739 draw_tentacle_1 (t, front_p, True);
740 glClear (GL_DEPTH_BUFFER_BIT);
742 # endif /* HAVE_POLYGONMODE */
744 draw_tentacle_1 (t, front_p, False);
749 move_tentacle (tentacle *t)
751 /* tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)]; */
755 int skip = t->nsegments * (1 - (arg_wiggliness + 0.5));
759 for (i = 0; i < t->nsegments; i++)
761 if (++tick >= skip || i == t->nsegments-1)
763 /* randomize the motion of this segment... */
765 double phi_range = M_PI * 0.8 * arg_flexibility;
766 double th_range = M_PI * 0.9 * arg_flexibility;
767 get_position (t->segments[i].rot, &x, &y, &z, True);
768 t->segments[i].phi = phi_range * (0.5 - y);
769 t->segments[i].th = th_range * (0.5 - z);
770 t->segments[i].length = ((0.8 + ((0.5 - x) * 0.4)) *
771 (arg_length / t->nsegments));
773 /* ...and make the previous N segments be interpolated
774 between this one and the previous randomized one. */
775 for (j = last+1; j <= i; j++)
777 t->segments[j].phi = (t->segments[i].phi / (i - last));
778 t->segments[j].th = (t->segments[i].th / (i - last));
779 t->segments[j].length = (t->segments[i].length);
785 len += t->segments[i].length;
788 /* thickness of segment is relative to current position on tentacle
789 (not just the index of the segment). */
790 for (i = 0; i < t->nsegments; i++)
794 double tt = (t->segments[0].thickness * (len - pos) / len);
795 if (tt < 0.001) tt = 0.001;
796 t->segments[i].thickness = tt;
798 pos += t->segments[i].length;
805 tentacles_handle_event (ModeInfo *mi, XEvent *event)
807 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
809 if (gltrackball_event_handler (event, tc->trackball,
810 MI_WIDTH (mi), MI_HEIGHT (mi),
813 else if (event->xany.type == KeyPress)
817 XLookupString (&event->xkey, &c, 1, &keysym, 0);
820 gltrackball_reset (tc->trackball, 0, 0);
830 parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
833 a[3] = 1.0; /* alpha */
835 if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
837 fprintf (stderr, "%s: can't parse %s color %s", progname, name, s);
840 a[0] = c.red / 65536.0;
841 a[1] = c.green / 65536.0;
842 a[2] = c.blue / 65536.0;
847 init_tentacles (ModeInfo *mi)
849 tentacles_configuration *tc;
850 int wire = MI_IS_WIREFRAME(mi);
855 tc = &tcs[MI_SCREEN(mi)];
857 tc->glx_context = init_GL(mi);
859 reshape_tentacles (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
863 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
864 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
865 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
866 glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
867 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
868 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
869 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
874 glEnable (GL_LIGHTING);
875 glEnable (GL_LIGHT0);
878 tc->trackball = gltrackball_init (False);
880 tc->left_p = !(random() % 5);
882 if (arg_segments < 2) arg_segments = 2;
883 if (arg_slices < 3) arg_slices = 3;
884 if (arg_thickness < 0.1) arg_thickness = 0.1;
885 if (arg_wiggliness < 0) arg_wiggliness = 0;
886 if (arg_wiggliness > 1) arg_wiggliness = 1;
887 if (arg_flexibility < 0) arg_flexibility = 0;
888 if (arg_flexibility > 1) arg_flexibility = 1;
890 parse_color (mi, "tentacleColor", arg_color, tc->tentacle_color);
891 parse_color (mi, "stripeColor", arg_stripe, tc->stripe_color);
892 parse_color (mi, "suckerColor", arg_sucker, tc->sucker_color);
894 /* Black outlines for light colors, white outlines for dark colors. */
895 if (tc->tentacle_color[0] + tc->tentacle_color[1] + tc->tentacle_color[2]
897 tc->outline_color[0] = 1;
898 tc->outline_color[1] = tc->outline_color[0];
899 tc->outline_color[2] = tc->outline_color[0];
900 tc->outline_color[3] = 1;
902 for (i = 0; i < MI_COUNT(mi); i++)
903 move_tentacle (make_tentacle (mi, i, MI_COUNT(mi)));
905 if (wire) texture_p = cel_p = False;
906 if (cel_p) texture_p = False;
908 if (texture_p || cel_p) {
909 glGenTextures(1, &tc->texid);
910 # ifdef HAVE_GLBINDTEXTURE
911 glBindTexture ((cel_p ? GL_TEXTURE_1D : GL_TEXTURE_2D), tc->texid);
923 tc->texture = XCreateImage (MI_DISPLAY(mi), MI_VISUAL(mi),
924 32, ZPixmap, 0, 0, w, 1, 32, 0);
925 tc->texture->data = (char *) calloc(1, tc->texture->bytes_per_line);
927 for (i = 0; i < 3; i++) XPutPixel (tc->texture, i, 0, 0xFF808080);
928 for (; i < 8; i++) XPutPixel (tc->texture, i, 0, 0xFFC0C0C0);
929 for (; i < w; i++) XPutPixel (tc->texture, i, 0, 0xFFFFFFFF);
932 tc->texture = image_data_to_ximage (MI_DISPLAY(mi), MI_VISUAL(mi),
933 scales_png, sizeof(scales_png));
935 if (!tc->texture) texture_p = cel_p = False;
939 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
941 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
942 tc->texture->width, tc->texture->height, 0,
943 GL_RGBA, GL_UNSIGNED_BYTE, tc->texture->data);
944 check_gl_error("texture");
946 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
947 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
949 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
950 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
952 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
954 glEnable(GL_TEXTURE_2D);
957 glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA,
958 tc->texture->width, 0,
959 GL_RGBA, GL_UNSIGNED_BYTE, tc->texture->data);
960 check_gl_error("texture");
962 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
963 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);
965 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
966 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
968 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
970 glEnable(GL_TEXTURE_1D);
971 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
972 glEnable (GL_LINE_SMOOTH);
973 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
976 /* Dark gray instead of black, so the outlines show up */
977 glClearColor (0.13, 0.13, 0.13, 1.0);
980 compute_unit_torus (mi, 0.5,
981 MAX(5, arg_slices/6),
982 MAX(9, arg_slices/3));
987 draw_tentacles (ModeInfo *mi)
989 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
990 Display *dpy = MI_DISPLAY(mi);
991 Window window = MI_WINDOW(mi);
994 if (!tc->glx_context)
997 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tc->glx_context));
999 glShadeModel(GL_SMOOTH);
1001 glEnable(GL_DEPTH_TEST);
1002 glEnable(GL_NORMALIZE);
1003 glEnable(GL_CULL_FACE);
1005 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1008 glRotatef(current_device_rotation(), 0, 0, 1);
1013 glPushAttrib (GL_ENABLE_BIT);
1015 { GLfloat s = 8.7/1600; glScalef(s,s,s); }
1016 glTranslatef(-800,-514,0);
1017 glDisable(GL_LIGHTING);
1018 glDisable(GL_TEXTURE_1D);
1019 glDisable(GL_TEXTURE_2D);
1020 glColor3f (1, 1, 1);
1021 glBegin(GL_LINE_LOOP);
1023 glVertex3f(0,1028,0);
1024 glVertex3f(1600,1028,0);
1025 glVertex3f(1600,0,0);
1031 gltrackball_rotate (tc->trackball);
1033 mi->polygon_count = 0;
1037 glPushAttrib (GL_ENABLE_BIT);
1038 glDisable (GL_LIGHTING);
1039 glDisable (GL_TEXTURE_1D);
1040 glDisable (GL_TEXTURE_2D);
1041 glColor3f (1, 1, 1);
1044 glVertex3f(-0.5, 0, 0); glVertex3f(0.5, 0, 0);
1045 glVertex3f(0, -0.5, 0); glVertex3f(0, 0.5, 0);
1056 glRotatef (ry, 0, 1, 0);
1057 glRotatef (rx, 1, 0, 0);
1058 glRotatef (rz, 0, 0, 1);
1060 glTranslatef (0, -2.0, -4.5);
1062 glTranslatef (0, -2.5, -5.0);
1065 if (!tc->button_down_p)
1066 for (i = 0; i < tc->ntentacles; i++)
1067 move_tentacle (tc->tentacles[i]);
1070 for (i = 0; i < tc->ntentacles; i++)
1073 glClear(GL_DEPTH_BUFFER_BIT);
1074 draw_tentacle (tc->tentacles[i], True);
1076 draw_tentacle (tc->tentacles[i], False);
1082 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1083 draw_sucker (tc->tentacles[0], True);
1086 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1087 glLineWidth (tc->line_thickness);
1088 glColor4fv (tc->outline_color);
1089 draw_sucker (tc->tentacles[0], False);
1095 if (mi->fps_p) do_fps (mi);
1098 glXSwapBuffers(dpy, window);
1101 XSCREENSAVER_MODULE_2 ("SkyTentacles", skytentacles, tentacles)