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 refresh_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;
175 glViewport (0, 0, (GLint) width, (GLint) height);
177 glMatrixMode(GL_PROJECTION);
179 gluPerspective (30.0, 1/h, 1.0, 100.0);
181 glMatrixMode(GL_MODELVIEW);
183 gluLookAt( 0.0, 0.0, 30.0,
187 glClear(GL_COLOR_BUFFER_BIT);
189 tc->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (3, width / 200));
194 normalize (GLfloat *x, GLfloat *y, GLfloat *z)
196 GLfloat d = sqrt((*x)*(*x) + (*y)*(*y) + (*z)*(*z));
203 dot (GLfloat x0, GLfloat y0, GLfloat z0,
204 GLfloat x1, GLfloat y1, GLfloat z1)
206 return x0*x1 + y0*y1 + z0*z1;
211 compute_unit_torus (ModeInfo *mi, double ratio, int slices1, int slices2)
213 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
214 Bool wire = MI_IS_WIREFRAME (mi);
217 if (wire) slices1 /= 2;
218 if (wire) slices2 /= 4;
219 if (slices1 < 3) slices1 = 3;
220 if (slices2 < 3) slices2 = 3;
222 tc->torus_polys = slices1 * (slices2+1) * 2;
223 tc->torus_points = (XYZ *) calloc (tc->torus_polys + 1,
224 sizeof (*tc->torus_points));
225 tc->torus_normals = (XYZ *) calloc (tc->torus_polys + 1,
226 sizeof (*tc->torus_normals));
227 tc->torus_step = 2 * (slices2+1);
229 for (i = 0; i < slices1; i++)
230 for (j = 0; j <= slices2; j++)
231 for (k = 0; k <= 1; k++)
233 double s = (i + k) % slices1 + 0.5;
234 double t = j % slices2;
236 p.x = cos(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
237 p.y = sin(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
238 p.z = sin(s*M_PI*2/slices1);
239 tc->torus_normals[fp] = p;
241 p.x = (1 + ratio * cos(s*M_PI*2/slices1)) * cos(t*M_PI*2/slices2) / 2;
242 p.y = (1 + ratio * cos(s*M_PI*2/slices1)) * sin(t*M_PI*2/slices2) / 2;
243 p.z = ratio * sin(s*M_PI*2/slices1) / 2;
244 tc->torus_points[fp] = p;
247 if (fp != tc->torus_polys) abort();
248 tc->torus_polys = fp;
252 /* Initializes a new tentacle and stores it in the list.
255 make_tentacle (ModeInfo *mi, int which, int total)
257 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
258 tentacle *t = (tentacle *) calloc (1, sizeof (*t));
264 /* position tentacles on a grid */
266 int cols = (int) (sqrt(total) + 0.5);
267 int rows = (total+cols-1) / cols;
268 int xx = which % cols;
269 int yy = which / cols;
270 double spc = arg_thickness * 0.8;
271 if (!intersect_p) cols = 1, xx = 0;
272 t->x = (cols * spc / 2) - (spc * (xx + 0.5));
273 t->y = (rows * spc / 2) - (spc * (yy + 0.5));
277 /* Brighten or darken the colors of this tentacle from the default.
279 brightness = 0.6 + frand(3.0);
280 memcpy (t->tentacle_color, tc->tentacle_color, 4 * sizeof(*t->tentacle_color));
281 memcpy (t->stripe_color, tc->stripe_color, 4 * sizeof(*t->stripe_color));
282 memcpy (t->sucker_color, tc->sucker_color, 4 * sizeof(*t->sucker_color));
284 t->X[0] *= brightness; if (t->X[0] > 1) t->X[0] = 1; \
285 t->X[1] *= brightness; if (t->X[1] > 1) t->X[1] = 1; \
286 t->X[2] *= brightness; if (t->X[2] > 1) t->X[2] = 1
287 FROB (tentacle_color);
292 t->nsegments = (arg_segments) + BELLRAND(arg_segments);
294 t->segments = (segment *) calloc (t->nsegments+1, sizeof(*t->segments));
295 for (i = 0; i < t->nsegments; i++)
297 double spin_speed = 0;
298 double spin_accel = 0;
299 double wander_speed = arg_speed * (0.02 + BELLRAND(0.1));
300 t->segments[i].rot = make_rotator (spin_speed, spin_speed, spin_speed,
301 spin_accel, wander_speed, True);
304 t->segments[0].thickness = (((arg_thickness * 0.5) +
305 BELLRAND(arg_thickness * 0.6))
308 if (tc->tentacles_size <= tc->ntentacles)
310 tc->tentacles_size = (tc->tentacles_size * 1.2) + tc->ntentacles + 2;
311 tc->tentacles = (tentacle **)
312 realloc (tc->tentacles, tc->tentacles_size * sizeof(*tc->tentacles));
315 fprintf (stderr, "%s: out of memory (%d tentacles)\n",
316 progname, tc->tentacles_size);
321 tc->tentacles[tc->ntentacles++] = t;
327 draw_sucker (tentacle *t, Bool front_p)
329 tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)];
330 Bool wire = MI_IS_WIREFRAME (t->mi);
332 int strips = tc->torus_polys / tc->torus_step;
335 glFrontFace (front_p ? GL_CW : GL_CCW);
336 for (i = 0; i < strips; i++)
338 int ii = i * tc->torus_step;
340 /* Leave off the polygons on the underside. This reduces polygon
341 count by about 10% with the default settings. */
342 if (strips > 4 && i >= strips/2 && i < strips-1)
345 glBegin (wire ? GL_LINE_STRIP : GL_QUAD_STRIP);
346 for (j = 0; j < tc->torus_step; j++)
348 XYZ sp = tc->torus_points[ii+j];
349 XYZ sn = tc->torus_normals[ii+j];
350 glNormal3f(sn.x, sn.y, sn.z);
351 glVertex3f(sp.x, sp.y, sp.z);
356 t->mi->polygon_count += points/2;
360 draw_tentacle_1 (tentacle *t, Bool front_p, Bool outline_p)
362 tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)];
364 Bool wire = MI_IS_WIREFRAME (t->mi);
365 XYZ ctr = { 0,0,0 }; /* current position of base of segment */
366 double cth = 0; /* overall orientation of current segment */
368 double cth_cos = 1, cth_sin = 0;
369 double cphi_cos = 1, cphi_sin = 0;
371 GLfloat light[3]; /* vector to the light */
373 GLfloat t0 = 0.0; /* texture coordinate */
375 XYZ *ring, *oring; /* points around the edge (this, and previous) */
376 XYZ *norm, *onorm; /* their normals */
377 XYZ *ucirc; /* unit circle, to save some trig */
379 /* Which portion of the radius the indented/colored stripe takes up */
380 int indented_points = arg_slices * 0.2;
382 /* We do rotation this way to minimize the number of calls to sin/cos.
383 We have to hack the transformations manually instead of using
384 glRotate/glTranslate because those calls are not allowed *inside*
385 of a glBegin/glEnd block...
387 # define ROT(P) do { \
389 _p.y = ((P.y * cth_sin - P.x * cth_cos)); \
390 _p.x = ((P.y * cth_cos + P.x * cth_sin) * cphi_sin - (P.z * cphi_cos)); \
391 _p.z = ((P.y * cth_cos + P.x * cth_sin) * cphi_cos + (P.z * cphi_sin)); \
395 ring = (XYZ *) malloc (arg_slices * sizeof(*ring));
396 norm = (XYZ *) malloc (arg_slices * sizeof(*norm));
397 oring = (XYZ *) malloc (arg_slices * sizeof(*oring));
398 onorm = (XYZ *) malloc (arg_slices * sizeof(*onorm));
399 ucirc = (XYZ *) malloc (arg_slices * sizeof(*ucirc));
401 light[0] = light_pos[0];
402 light[1] = light_pos[1];
403 light[2] = light_pos[2];
404 normalize (&light[0], &light[1], &light[2]);
406 for (i = 0; i < arg_slices; i++)
408 double a = M_PI * 2 * i / arg_slices;
415 # ifdef HAVE_POLYGONMODE
417 glPolygonMode (GL_FRONT_AND_BACK, (front_p ? GL_FILL : GL_LINE));
421 glTranslatef (t->x, t->y, t->z);
425 glPushAttrib (GL_ENABLE_BIT);
426 glDisable (GL_LIGHTING);
427 glDisable (GL_TEXTURE_1D);
428 glDisable (GL_TEXTURE_2D);
431 glBegin(GL_LINE_LOOP);
432 for (i = 0; i < arg_slices; i++)
433 glVertex3f (arg_thickness / 2 * cos (M_PI * 2 * i / arg_slices),
434 arg_thickness / 2 * sin (M_PI * 2 * i / arg_slices),
440 if (!front_p || outline_p)
441 glColor4fv (tc->outline_color);
443 glColor4fv (t->tentacle_color);
446 static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
447 static const GLfloat bshiny = 128.0;
448 glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
449 glMateriali (GL_FRONT, GL_SHININESS, bshiny);
452 for (i = 0; i < t->nsegments; i++)
455 GLfloat t1 = t0 + i / (t->nsegments * M_PI * 2);
457 for (j = 0; j < arg_slices; j++)
459 /* Construct a vertical disc at the origin, to use as the
460 base of this segment.
462 double r = t->segments[i].thickness / 2;
464 if (j <= indented_points/2 || j >= arg_slices-indented_points/2)
465 r *= 0.75; /* indent the stripe */
470 ring[j].x = r * ucirc[j].x;
472 ring[j].z = r * ucirc[j].y;
474 /* Then rotate the points by the angle of the current segment. */
477 /* Then move the ring to the base of this segment. */
484 /* Compute the normals of the faces on this segment. We do this
485 first so that the normals of the vertexes can be the average
486 of the normals of the faces.
487 #### Uh, except I didn't actually implement that...
488 but it would be a good idea.
491 for (j = 0; j <= arg_slices; j++)
493 int j0 = j % arg_slices;
494 int j1 = (j+1) % arg_slices;
495 norm[j0] = calc_normal (oring[j0], ring[j0], ring[j1]);
503 glLineWidth (tc->line_thickness);
504 glFrontFace (front_p ? GL_CCW : GL_CW);
505 glBegin (wire ? GL_LINES : smooth_p ? GL_QUAD_STRIP : GL_QUADS);
506 for (j = 0; j <= arg_slices; j++)
508 int j0 = j % arg_slices;
509 int j1 = (j+1) % arg_slices;
511 GLfloat ts = j / (double) arg_slices;
513 if (!front_p || outline_p)
514 glColor4fv (tc->outline_color);
515 else if (j <= indented_points/2 ||
516 j >= arg_slices-indented_points/2)
518 glColor4fv (t->stripe_color);
519 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
524 glColor4fv (t->tentacle_color);
525 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
529 /* For cel shading, the 1d texture coordinate (s) is the
530 dot product of the lighting vector and the vertex normal.
534 t0 = dot (light[0], light[1], light[2],
535 onorm[j0].x, onorm[j0].y, onorm[j0].z);
536 t1 = dot (light[0], light[1], light[2],
537 norm[j0].x, norm[j0].y, norm[j0].z);
542 glTexCoord2f (t0, ts);
543 glNormal3f (onorm[j0].x, onorm[j0].y, onorm[j0].z);
544 glVertex3f (oring[j0].x, oring[j0].y, oring[j0].z);
546 glTexCoord2f (t1, ts);
547 glNormal3f ( norm[j0].x, norm[j0].y, norm[j0].z);
548 glVertex3f ( ring[j0].x, ring[j0].y, ring[j0].z);
552 ts = j1 / (double) arg_slices;
553 glTexCoord2f (t1, ts);
554 glVertex3f ( ring[j1].x, ring[j1].y, ring[j1].z);
555 glTexCoord2f (t0, ts);
556 glVertex3f (oring[j1].x, oring[j1].y, oring[j1].z);
558 t->mi->polygon_count++;
564 glBegin (GL_LINE_LOOP);
565 for (j = 0; j < arg_slices; j++)
566 glVertex3f (ring[j].x, ring[j].y, ring[j].z);
570 /* Now draw the suckers!
573 double seg_length = arg_length / t->nsegments;
574 double sucker_size = arg_thickness / 5;
575 double sucker_spacing = sucker_size * 1.3;
576 int nsuckers = seg_length / sucker_spacing;
577 double oth = cth - t->segments[i-1].th;
578 double ophi = cphi - t->segments[i-1].phi;
582 glLineWidth (MAX (2, tc->line_thickness / 2.0));
583 glDisable (GL_TEXTURE_2D);
585 /* Sometimes we have N suckers on one segment;
586 sometimes we have one sucker every N segments. */
589 int segs_per_sucker =
590 (int) ((sucker_spacing / seg_length) + 0.5);
591 nsuckers = (i % segs_per_sucker) ? 0 : 1;
596 glColor4fv (tc->outline_color);
597 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
602 glColor4fv (t->sucker_color);
603 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
607 for (k = 0; k < nsuckers; k++)
613 p.x = p0.x + (p1.x - p0.x) * (k + 0.5) / nsuckers;
614 p.y = p0.y + (p1.y - p0.y) * (k + 0.5) / nsuckers;
615 p.z = p0.z + (p1.z - p0.z) * (k + 0.5) / nsuckers;
618 glTranslatef (p.x, p.y, p.z);
619 glRotatef (ophi * 180 / M_PI, 0, 1, 0);
620 glRotatef (-oth * 180 / M_PI, 1, 0, 0);
621 glRotatef (90, 1, 0, 0);
623 { /* Not quite right: this is the slope of the outer edge
624 if the next segment was not tilted at all... If there
625 is any tilt, then the angle of this wall and the
626 opposite wall are very different.
628 double slope = ((t->segments[i-1].thickness -
629 t->segments[i].thickness) /
630 t->segments[i].length);
631 glRotatef (-45 * slope, 1, 0, 0);
634 scale = t->segments[i].thickness / arg_thickness;
635 scale *= 0.7 * sucker_size;
637 glScalef (scale, scale, scale * 4);
639 glTranslatef (0, 0, -0.1); /* embed */
644 glScalef (scale, scale, scale);
647 glTranslatef (1, 0, 0); /* left */
648 draw_sucker (t, front_p);
650 glTranslatef (-2, 0, 0); /* right */
651 draw_sucker (t, front_p);
656 if (texture_p) glEnable (GL_TEXTURE_2D);
660 /* Now draw the end caps.
662 glLineWidth (tc->line_thickness);
663 if (!outline_p && (i == 0 || i == t->nsegments-1))
666 GLfloat ctrz = ctr.z + ((i == 0 ? -1 : 1) *
667 t->segments[i].thickness / 4);
670 glColor4fv (t->tentacle_color);
671 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
674 glFrontFace ((front_p ? i == 0 : i != 0) ? GL_CCW : GL_CW);
675 glBegin (wire ? GL_LINES : GL_TRIANGLE_FAN);
676 glNormal3f (0, 0, (i == 0 ? -1 : 1));
677 glTexCoord2f (t0 - 0.25, 0.5);
678 glVertex3f (ctr.x, ctr.y, ctrz);
679 for (j = 0; j <= arg_slices; j++)
681 int jj = j % arg_slices;
682 GLfloat ts = j / (double) arg_slices;
683 glTexCoord2f (t0, ts);
684 glNormal3f (norm[jj].x, norm[jj].y, norm[jj].z);
685 glVertex3f (ring[jj].x, ring[jj].y, ring[jj].z);
686 if (wire) glVertex3f (ctr.x, ctr.y, ctrz);
687 t->mi->polygon_count++;
692 /* Now move to the end of this segment in preparation for the next.
694 if (i != t->nsegments-1)
698 p.y = t->segments[i].length;
705 /* Accumulate the current angle and rotation, to keep track of the
706 rotation of the upcoming segment.
708 cth += t->segments[i].th;
709 cphi += t->segments[i].phi;
713 cphi_sin = sin (cphi);
714 cphi_cos = cos (cphi);
716 memcpy (oring, ring, arg_slices * sizeof(*ring));
717 memcpy (onorm, norm, arg_slices * sizeof(*norm));
734 draw_tentacle (tentacle *t, Bool front_p)
736 # ifndef HAVE_POLYGONMODE
737 Bool wire = MI_IS_WIREFRAME (t->mi);
738 if (!wire && cel_p && front_p)
740 draw_tentacle_1 (t, front_p, True);
741 glClear (GL_DEPTH_BUFFER_BIT);
743 # endif /* HAVE_POLYGONMODE */
745 draw_tentacle_1 (t, front_p, False);
750 move_tentacle (tentacle *t)
752 /* tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)]; */
756 int skip = t->nsegments * (1 - (arg_wiggliness + 0.5));
760 for (i = 0; i < t->nsegments; i++)
762 if (++tick >= skip || i == t->nsegments-1)
764 /* randomize the motion of this segment... */
766 double phi_range = M_PI * 0.8 * arg_flexibility;
767 double th_range = M_PI * 0.9 * arg_flexibility;
768 get_position (t->segments[i].rot, &x, &y, &z, True);
769 t->segments[i].phi = phi_range * (0.5 - y);
770 t->segments[i].th = th_range * (0.5 - z);
771 t->segments[i].length = ((0.8 + ((0.5 - x) * 0.4)) *
772 (arg_length / t->nsegments));
774 /* ...and make the previous N segments be interpolated
775 between this one and the previous randomized one. */
776 for (j = last+1; j <= i; j++)
778 t->segments[j].phi = (t->segments[i].phi / (i - last));
779 t->segments[j].th = (t->segments[i].th / (i - last));
780 t->segments[j].length = (t->segments[i].length);
786 len += t->segments[i].length;
789 /* thickness of segment is relative to current position on tentacle
790 (not just the index of the segment). */
791 for (i = 0; i < t->nsegments; i++)
795 double tt = (t->segments[0].thickness * (len - pos) / len);
796 if (tt < 0.001) tt = 0.001;
797 t->segments[i].thickness = tt;
799 pos += t->segments[i].length;
806 tentacles_handle_event (ModeInfo *mi, XEvent *event)
808 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
810 if (gltrackball_event_handler (event, tc->trackball,
811 MI_WIDTH (mi), MI_HEIGHT (mi),
814 else if (event->xany.type == KeyPress)
818 XLookupString (&event->xkey, &c, 1, &keysym, 0);
821 gltrackball_reset (tc->trackball);
831 parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
834 a[3] = 1.0; /* alpha */
836 if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
838 fprintf (stderr, "%s: can't parse %s color %s", progname, name, s);
841 a[0] = c.red / 65536.0;
842 a[1] = c.green / 65536.0;
843 a[2] = c.blue / 65536.0;
848 init_tentacles (ModeInfo *mi)
850 tentacles_configuration *tc;
851 int wire = MI_IS_WIREFRAME(mi);
855 tcs = (tentacles_configuration *)
856 calloc (MI_NUM_SCREENS(mi), sizeof (tentacles_configuration));
858 fprintf(stderr, "%s: out of memory\n", progname);
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)