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, 0, 0);
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);
854 MI_INIT (mi, tcs, NULL);
856 tc = &tcs[MI_SCREEN(mi)];
858 tc->glx_context = init_GL(mi);
860 reshape_tentacles (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
864 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
865 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
866 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
867 glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
868 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
869 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
870 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
875 glEnable (GL_LIGHTING);
876 glEnable (GL_LIGHT0);
879 tc->trackball = gltrackball_init (False);
881 tc->left_p = !(random() % 5);
883 if (arg_segments < 2) arg_segments = 2;
884 if (arg_slices < 3) arg_slices = 3;
885 if (arg_thickness < 0.1) arg_thickness = 0.1;
886 if (arg_wiggliness < 0) arg_wiggliness = 0;
887 if (arg_wiggliness > 1) arg_wiggliness = 1;
888 if (arg_flexibility < 0) arg_flexibility = 0;
889 if (arg_flexibility > 1) arg_flexibility = 1;
891 parse_color (mi, "tentacleColor", arg_color, tc->tentacle_color);
892 parse_color (mi, "stripeColor", arg_stripe, tc->stripe_color);
893 parse_color (mi, "suckerColor", arg_sucker, tc->sucker_color);
895 /* Black outlines for light colors, white outlines for dark colors. */
896 if (tc->tentacle_color[0] + tc->tentacle_color[1] + tc->tentacle_color[2]
898 tc->outline_color[0] = 1;
899 tc->outline_color[1] = tc->outline_color[0];
900 tc->outline_color[2] = tc->outline_color[0];
901 tc->outline_color[3] = 1;
903 for (i = 0; i < MI_COUNT(mi); i++)
904 move_tentacle (make_tentacle (mi, i, MI_COUNT(mi)));
906 if (wire) texture_p = cel_p = False;
907 if (cel_p) texture_p = False;
909 if (texture_p || cel_p) {
910 glGenTextures(1, &tc->texid);
911 # ifdef HAVE_GLBINDTEXTURE
912 glBindTexture ((cel_p ? GL_TEXTURE_1D : GL_TEXTURE_2D), tc->texid);
915 tc->texture = xpm_to_ximage (MI_DISPLAY(mi), MI_VISUAL(mi),
917 (cel_p ? grey_texture : scales));
918 if (!tc->texture) texture_p = cel_p = False;
922 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
924 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
925 tc->texture->width, tc->texture->height, 0,
927 /* GL_UNSIGNED_BYTE, */
928 GL_UNSIGNED_INT_8_8_8_8_REV,
930 check_gl_error("texture");
932 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
933 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
935 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
936 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
938 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
940 glEnable(GL_TEXTURE_2D);
943 glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA,
944 tc->texture->width, 0,
946 /* GL_UNSIGNED_BYTE, */
947 GL_UNSIGNED_INT_8_8_8_8_REV,
949 check_gl_error("texture");
951 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
952 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);
954 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
955 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
957 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
959 glEnable(GL_TEXTURE_1D);
960 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
961 glEnable (GL_LINE_SMOOTH);
962 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
965 /* Dark gray instead of black, so the outlines show up */
966 glClearColor (0.13, 0.13, 0.13, 1.0);
969 compute_unit_torus (mi, 0.5,
970 MAX(5, arg_slices/6),
971 MAX(9, arg_slices/3));
976 draw_tentacles (ModeInfo *mi)
978 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
979 Display *dpy = MI_DISPLAY(mi);
980 Window window = MI_WINDOW(mi);
983 if (!tc->glx_context)
986 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tc->glx_context));
988 glShadeModel(GL_SMOOTH);
990 glEnable(GL_DEPTH_TEST);
991 glEnable(GL_NORMALIZE);
992 glEnable(GL_CULL_FACE);
994 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
997 glRotatef(current_device_rotation(), 0, 0, 1);
1002 glPushAttrib (GL_ENABLE_BIT);
1004 { GLfloat s = 8.7/1600; glScalef(s,s,s); }
1005 glTranslatef(-800,-514,0);
1006 glDisable(GL_LIGHTING);
1007 glDisable(GL_TEXTURE_1D);
1008 glDisable(GL_TEXTURE_2D);
1009 glColor3f (1, 1, 1);
1010 glBegin(GL_LINE_LOOP);
1012 glVertex3f(0,1028,0);
1013 glVertex3f(1600,1028,0);
1014 glVertex3f(1600,0,0);
1020 gltrackball_rotate (tc->trackball);
1022 mi->polygon_count = 0;
1026 glPushAttrib (GL_ENABLE_BIT);
1027 glDisable (GL_LIGHTING);
1028 glDisable (GL_TEXTURE_1D);
1029 glDisable (GL_TEXTURE_2D);
1030 glColor3f (1, 1, 1);
1033 glVertex3f(-0.5, 0, 0); glVertex3f(0.5, 0, 0);
1034 glVertex3f(0, -0.5, 0); glVertex3f(0, 0.5, 0);
1045 glRotatef (ry, 0, 1, 0);
1046 glRotatef (rx, 1, 0, 0);
1047 glRotatef (rz, 0, 0, 1);
1049 glTranslatef (0, -2.0, -4.5);
1051 glTranslatef (0, -2.5, -5.0);
1054 if (!tc->button_down_p)
1055 for (i = 0; i < tc->ntentacles; i++)
1056 move_tentacle (tc->tentacles[i]);
1059 for (i = 0; i < tc->ntentacles; i++)
1062 glClear(GL_DEPTH_BUFFER_BIT);
1063 draw_tentacle (tc->tentacles[i], True);
1065 draw_tentacle (tc->tentacles[i], False);
1071 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1072 draw_sucker (tc->tentacles[0], True);
1075 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1076 glLineWidth (tc->line_thickness);
1077 glColor4fv (tc->outline_color);
1078 draw_sucker (tc->tentacles[0], False);
1084 if (mi->fps_p) do_fps (mi);
1087 glXSwapBuffers(dpy, window);
1090 XSCREENSAVER_MODULE_2 ("SkyTentacles", skytentacles, tentacles)