1 /* Sky Tentacles, Copyright (c) 2008-2012 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 */
42 #define DEF_SPEED "1.0"
43 #define DEF_SMOOTH "True"
44 #define DEF_TEXTURE "True"
45 #define DEF_CEL "False"
46 #define DEF_INTERSECT "False"
47 #define DEF_SLICES "16"
48 #define DEF_SEGMENTS "24"
49 #define DEF_WIGGLINESS "0.35"
50 #define DEF_FLEXIBILITY "0.35"
51 #define DEF_THICKNESS "1.0"
52 #define DEF_LENGTH "9.0"
53 #define DEF_COLOR "#305A30"
54 #define DEF_STRIPE "#451A30"
55 #define DEF_SUCKER "#453E30"
56 #define DEF_DEBUG "False"
58 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
61 GLfloat length; /* length of the segment coming out of this segment */
62 GLfloat th; /* vector tilt (on yz plane) from previous segment */
63 GLfloat phi; /* vector rotation (on xy plane) from previous segment */
64 GLfloat thickness; /* radius of tentacle at the bottom of this segment */
65 rotator *rot; /* motion modeller */
70 GLfloat x, y, z; /* position of the base */
73 GLfloat tentacle_color[4], stripe_color[4], sucker_color[4];
77 GLXContext *glx_context;
78 trackball_state *trackball;
84 GLfloat tentacle_color[4], stripe_color[4], sucker_color[4];
91 GLfloat line_thickness;
92 GLfloat outline_color[4];
99 } tentacles_configuration;
101 static tentacles_configuration *tcs = NULL;
104 static GLfloat arg_speed;
106 static int texture_p;
108 static int intersect_p;
109 static int arg_slices;
110 static int arg_segments;
111 static GLfloat arg_thickness;
112 static GLfloat arg_length;
113 static GLfloat arg_wiggliness;
114 static GLfloat arg_flexibility;
115 static char *arg_color, *arg_stripe, *arg_sucker;
117 /* we can only have one light when doing cel shading */
118 static GLfloat light_pos[4] = {1.0, 1.0, 1.0, 0.0};
121 static XrmOptionDescRec opts[] = {
122 { "-speed", ".speed", XrmoptionSepArg, 0 },
123 { "-no-smooth", ".smooth", XrmoptionNoArg, "False" },
124 { "-texture", ".texture", XrmoptionNoArg, "True" },
125 { "-no-texture", ".texture", XrmoptionNoArg, "False" },
126 { "-cel", ".cel", XrmoptionNoArg, "True" },
127 { "-no-cel", ".cel", XrmoptionNoArg, "False" },
128 { "-intersect", ".intersect", XrmoptionNoArg, "True" },
129 { "-no-intersect", ".intersect", XrmoptionNoArg, "False" },
130 { "-slices", ".slices", XrmoptionSepArg, 0 },
131 { "-segments", ".segments", XrmoptionSepArg, 0 },
132 { "-thickness", ".thickness", XrmoptionSepArg, 0 },
133 { "-length", ".length", XrmoptionSepArg, 0 },
134 { "-wiggliness", ".wiggliness", XrmoptionSepArg, 0 },
135 { "-flexibility", ".flexibility", XrmoptionSepArg, 0 },
136 { "-color", ".tentacleColor", XrmoptionSepArg, 0 },
137 { "-stripe-color", ".stripeColor", XrmoptionSepArg, 0 },
138 { "-sucker-color", ".suckerColor", XrmoptionSepArg, 0 },
139 { "-debug", ".debug", XrmoptionNoArg, "True" },
142 static argtype vars[] = {
143 {&arg_speed, "speed", "Speed", DEF_SPEED, t_Float},
144 {&smooth_p, "smooth", "Smooth", DEF_SMOOTH, t_Bool},
145 {&texture_p, "texture", "Texture", DEF_TEXTURE, t_Bool},
146 {&cel_p, "cel", "Cel", DEF_CEL, t_Bool},
147 {&intersect_p, "intersect", "Intersect", DEF_INTERSECT, t_Bool},
148 {&arg_slices, "slices", "Slices", DEF_SLICES, t_Int},
149 {&arg_segments, "segments", "Segments", DEF_SEGMENTS, t_Int},
150 {&arg_thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
151 {&arg_length, "length", "Length", DEF_LENGTH, t_Float},
152 {&arg_wiggliness, "wiggliness", "Wiggliness", DEF_WIGGLINESS, t_Float},
153 {&arg_flexibility, "flexibility", "Flexibility", DEF_FLEXIBILITY, t_Float},
154 {&arg_color, "tentacleColor", "Color", DEF_COLOR, t_String},
155 {&arg_stripe, "stripeColor", "Color", DEF_STRIPE, t_String},
156 {&arg_sucker, "suckerColor", "Color", DEF_SUCKER, t_String},
157 {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
160 ENTRYPOINT ModeSpecOpt tentacles_opts = {countof(opts), opts, countof(vars), vars, NULL};
163 /* Window management, etc
166 reshape_tentacles (ModeInfo *mi, int width, int height)
168 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
169 GLfloat h = (GLfloat) height / (GLfloat) width;
171 glViewport (0, 0, (GLint) width, (GLint) height);
173 glMatrixMode(GL_PROJECTION);
175 gluPerspective (30.0, 1/h, 1.0, 100.0);
177 glMatrixMode(GL_MODELVIEW);
179 gluLookAt( 0.0, 0.0, 30.0,
183 glClear(GL_COLOR_BUFFER_BIT);
185 tc->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (3, width / 200));
190 normalize (GLfloat *x, GLfloat *y, GLfloat *z)
192 GLfloat d = sqrt((*x)*(*x) + (*y)*(*y) + (*z)*(*z));
199 dot (GLfloat x0, GLfloat y0, GLfloat z0,
200 GLfloat x1, GLfloat y1, GLfloat z1)
202 return x0*x1 + y0*y1 + z0*z1;
207 compute_unit_torus (ModeInfo *mi, double ratio, int slices1, int slices2)
209 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
210 Bool wire = MI_IS_WIREFRAME (mi);
213 if (wire) slices1 /= 2;
214 if (wire) slices2 /= 4;
215 if (slices1 < 3) slices1 = 3;
216 if (slices2 < 3) slices2 = 3;
218 tc->torus_polys = slices1 * (slices2+1) * 2;
219 tc->torus_points = (XYZ *) calloc (tc->torus_polys + 1,
220 sizeof (*tc->torus_points));
221 tc->torus_normals = (XYZ *) calloc (tc->torus_polys + 1,
222 sizeof (*tc->torus_normals));
223 tc->torus_step = 2 * (slices2+1);
225 for (i = 0; i < slices1; i++)
226 for (j = 0; j <= slices2; j++)
227 for (k = 0; k <= 1; k++)
229 double s = (i + k) % slices1 + 0.5;
230 double t = j % slices2;
232 p.x = cos(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
233 p.y = sin(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
234 p.z = sin(s*M_PI*2/slices1);
235 tc->torus_normals[fp] = p;
237 p.x = (1 + ratio * cos(s*M_PI*2/slices1)) * cos(t*M_PI*2/slices2) / 2;
238 p.y = (1 + ratio * cos(s*M_PI*2/slices1)) * sin(t*M_PI*2/slices2) / 2;
239 p.z = ratio * sin(s*M_PI*2/slices1) / 2;
240 tc->torus_points[fp] = p;
243 if (fp != tc->torus_polys) abort();
244 tc->torus_polys = fp;
248 /* Initializes a new tentacle and stores it in the list.
251 make_tentacle (ModeInfo *mi, int which, int total)
253 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
254 tentacle *t = (tentacle *) calloc (1, sizeof (*t));
260 /* position tentacles on a grid */
262 int cols = (int) (sqrt(total) + 0.5);
263 int rows = (total+cols-1) / cols;
264 int xx = which % cols;
265 int yy = which / cols;
266 double spc = arg_thickness * 0.8;
267 if (!intersect_p) cols = 1, xx = 0;
268 t->x = (cols * spc / 2) - (spc * (xx + 0.5));
269 t->y = (rows * spc / 2) - (spc * (yy + 0.5));
273 /* Brighten or darken the colors of this tentacle from the default.
275 brightness = 0.6 + frand(3.0);
276 memcpy (t->tentacle_color, tc->tentacle_color, 4 * sizeof(*t->tentacle_color));
277 memcpy (t->stripe_color, tc->stripe_color, 4 * sizeof(*t->stripe_color));
278 memcpy (t->sucker_color, tc->sucker_color, 4 * sizeof(*t->sucker_color));
280 t->X[0] *= brightness; if (t->X[0] > 1) t->X[0] = 1; \
281 t->X[1] *= brightness; if (t->X[1] > 1) t->X[1] = 1; \
282 t->X[2] *= brightness; if (t->X[2] > 1) t->X[2] = 1
283 FROB (tentacle_color);
288 t->nsegments = (arg_segments) + BELLRAND(arg_segments);
290 t->segments = (segment *) calloc (t->nsegments+1, sizeof(*t->segments));
291 for (i = 0; i < t->nsegments; i++)
293 double spin_speed = 0;
294 double spin_accel = 0;
295 double wander_speed = arg_speed * (0.02 + BELLRAND(0.1));
296 t->segments[i].rot = make_rotator (spin_speed, spin_speed, spin_speed,
297 spin_accel, wander_speed, True);
300 t->segments[0].thickness = (((arg_thickness * 0.5) +
301 BELLRAND(arg_thickness * 0.6))
304 if (tc->tentacles_size <= tc->ntentacles)
306 tc->tentacles_size = (tc->tentacles_size * 1.2) + tc->ntentacles + 2;
307 tc->tentacles = (tentacle **)
308 realloc (tc->tentacles, tc->tentacles_size * sizeof(*tc->tentacles));
311 fprintf (stderr, "%s: out of memory (%d tentacles)\n",
312 progname, tc->tentacles_size);
317 tc->tentacles[tc->ntentacles++] = t;
323 draw_sucker (tentacle *t, Bool front_p)
325 tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)];
326 Bool wire = MI_IS_WIREFRAME (t->mi);
328 int strips = tc->torus_polys / tc->torus_step;
331 glFrontFace (front_p ? GL_CW : GL_CCW);
332 for (i = 0; i < strips; i++)
334 int ii = i * tc->torus_step;
336 /* Leave off the polygons on the underside. This reduces polygon
337 count by about 10% with the default settings. */
338 if (strips > 4 && i >= strips/2 && i < strips-1)
341 glBegin (wire ? GL_LINE_STRIP : GL_QUAD_STRIP);
342 for (j = 0; j < tc->torus_step; j++)
344 XYZ sp = tc->torus_points[ii+j];
345 XYZ sn = tc->torus_normals[ii+j];
346 glNormal3f(sn.x, sn.y, sn.z);
347 glVertex3f(sp.x, sp.y, sp.z);
352 t->mi->polygon_count += points/2;
357 draw_tentacle (tentacle *t, Bool front_p)
359 tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)];
361 Bool wire = MI_IS_WIREFRAME (t->mi);
362 XYZ ctr = { 0,0,0 }; /* current position of base of segment */
363 double cth = 0; /* overall orientation of current segment */
365 double cth_cos = 1, cth_sin = 0;
366 double cphi_cos = 1, cphi_sin = 0;
368 GLfloat light[3]; /* vector to the light */
370 GLfloat t0 = 0.0; /* texture coordinate */
372 XYZ *ring, *oring; /* points around the edge (this, and previous) */
373 XYZ *norm, *onorm; /* their normals */
374 XYZ *ucirc; /* unit circle, to save some trig */
376 /* Which portion of the radius the indented/colored stripe takes up */
377 int indented_points = arg_slices * 0.2;
379 /* We do rotation this way to minimize the number of calls to sin/cos.
380 We have to hack the transformations manually instead of using
381 glRotate/glTranslate because those calls are not allowed *inside*
382 of a glBegin/glEnd block...
384 # define ROT(P) do { \
386 _p.y = ((P.y * cth_sin - P.x * cth_cos)); \
387 _p.x = ((P.y * cth_cos + P.x * cth_sin) * cphi_sin - (P.z * cphi_cos)); \
388 _p.z = ((P.y * cth_cos + P.x * cth_sin) * cphi_cos + (P.z * cphi_sin)); \
392 ring = (XYZ *) malloc (arg_slices * sizeof(*ring));
393 norm = (XYZ *) malloc (arg_slices * sizeof(*norm));
394 oring = (XYZ *) malloc (arg_slices * sizeof(*oring));
395 onorm = (XYZ *) malloc (arg_slices * sizeof(*onorm));
396 ucirc = (XYZ *) malloc (arg_slices * sizeof(*ucirc));
398 light[0] = light_pos[0];
399 light[1] = light_pos[1];
400 light[2] = light_pos[2];
401 normalize (&light[0], &light[1], &light[2]);
403 for (i = 0; i < arg_slices; i++)
405 double a = M_PI * 2 * i / arg_slices;
413 glPolygonMode (GL_FRONT_AND_BACK, (front_p ? GL_FILL : GL_LINE));
416 glTranslatef (t->x, t->y, t->z);
420 glPushAttrib (GL_ENABLE_BIT);
421 glDisable (GL_LIGHTING);
422 glDisable (GL_TEXTURE_1D);
423 glDisable (GL_TEXTURE_2D);
426 glBegin(GL_LINE_LOOP);
427 for (i = 0; i < arg_slices; i++)
428 glVertex3f (arg_thickness / 2 * cos (M_PI * 2 * i / arg_slices),
429 arg_thickness / 2 * sin (M_PI * 2 * i / arg_slices),
436 glColor4fv (tc->outline_color);
438 glColor4fv (t->tentacle_color);
441 static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
442 static const GLfloat bshiny = 128.0;
443 glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
444 glMateriali (GL_FRONT, GL_SHININESS, bshiny);
447 for (i = 0; i < t->nsegments; i++)
450 GLfloat t1 = t0 + i / (t->nsegments * M_PI * 2);
452 for (j = 0; j < arg_slices; j++)
454 /* Construct a vertical disc at the origin, to use as the
455 base of this segment.
457 double r = t->segments[i].thickness / 2;
459 if (j <= indented_points/2 || j >= arg_slices-indented_points/2)
460 r *= 0.75; /* indent the stripe */
462 ring[j].x = r * ucirc[j].x;
464 ring[j].z = r * ucirc[j].y;
466 /* Then rotate the points by the angle of the current segment. */
469 /* Then move the ring to the base of this segment. */
476 /* Compute the normals of the faces on this segment. We do this
477 first so that the normals of the vertexes can be the average
478 of the normals of the faces.
479 #### Uh, except I didn't actually implement that...
480 but it would be a good idea.
483 for (j = 0; j <= arg_slices; j++)
485 int j0 = j % arg_slices;
486 int j1 = (j+1) % arg_slices;
487 norm[j0] = calc_normal (oring[j0], ring[j0], ring[j1]);
495 glLineWidth (tc->line_thickness);
496 glFrontFace (front_p ? GL_CCW : GL_CW);
497 glBegin (wire ? GL_LINES : smooth_p ? GL_QUAD_STRIP : GL_QUADS);
498 for (j = 0; j <= arg_slices; j++)
500 int j0 = j % arg_slices;
501 int j1 = (j+1) % arg_slices;
503 GLfloat ts = j / (double) arg_slices;
506 glColor4fv (tc->outline_color);
507 else if (j <= indented_points/2 ||
508 j >= arg_slices-indented_points/2)
510 glColor4fv (t->stripe_color);
511 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
516 glColor4fv (t->tentacle_color);
517 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
521 /* For cel shading, the 1d texture coordinate (s) is the
522 dot product of the lighting vector and the vertex normal.
526 t0 = dot (light[0], light[1], light[2],
527 onorm[j0].x, onorm[j0].y, onorm[j0].z);
528 t1 = dot (light[0], light[1], light[2],
529 norm[j0].x, norm[j0].y, norm[j0].z);
534 glTexCoord2f (t0, ts);
535 glNormal3f (onorm[j0].x, onorm[j0].y, onorm[j0].z);
536 glVertex3f (oring[j0].x, oring[j0].y, oring[j0].z);
538 glTexCoord2f (t1, ts);
539 glNormal3f ( norm[j0].x, norm[j0].y, norm[j0].z);
540 glVertex3f ( ring[j0].x, ring[j0].y, ring[j0].z);
544 ts = j1 / (double) arg_slices;
545 glTexCoord2f (t1, ts);
546 glVertex3f ( ring[j1].x, ring[j1].y, ring[j1].z);
547 glTexCoord2f (t0, ts);
548 glVertex3f (oring[j1].x, oring[j1].y, oring[j1].z);
550 t->mi->polygon_count++;
556 glBegin (GL_LINE_LOOP);
557 for (j = 0; j < arg_slices; j++)
558 glVertex3f (ring[j].x, ring[j].y, ring[j].z);
562 /* Now draw the suckers!
565 double seg_length = arg_length / t->nsegments;
566 double sucker_size = arg_thickness / 5;
567 double sucker_spacing = sucker_size * 1.3;
568 int nsuckers = seg_length / sucker_spacing;
569 double oth = cth - t->segments[i-1].th;
570 double ophi = cphi - t->segments[i-1].phi;
574 glLineWidth (MAX (2, tc->line_thickness / 2.0));
575 glDisable (GL_TEXTURE_2D);
577 /* Sometimes we have N suckers on one segment;
578 sometimes we have one sucker every N segments. */
581 int segs_per_sucker =
582 (int) ((sucker_spacing / seg_length) + 0.5);
583 nsuckers = (i % segs_per_sucker) ? 0 : 1;
588 glColor4fv (t->sucker_color);
589 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
593 for (k = 0; k < nsuckers; k++)
599 p.x = p0.x + (p1.x - p0.x) * (k + 0.5) / nsuckers;
600 p.y = p0.y + (p1.y - p0.y) * (k + 0.5) / nsuckers;
601 p.z = p0.z + (p1.z - p0.z) * (k + 0.5) / nsuckers;
604 glTranslatef (p.x, p.y, p.z);
605 glRotatef (ophi * 180 / M_PI, 0, 1, 0);
606 glRotatef (-oth * 180 / M_PI, 1, 0, 0);
607 glRotatef (90, 1, 0, 0);
609 { /* Not quite right: this is the slope of the outer edge
610 if the next segment was not tilted at all... If there
611 is any tilt, then the angle of this wall and the
612 opposite wall are very different.
614 double slope = ((t->segments[i-1].thickness -
615 t->segments[i].thickness) /
616 t->segments[i].length);
617 glRotatef (-45 * slope, 1, 0, 0);
620 scale = t->segments[i].thickness / arg_thickness;
621 scale *= 0.7 * sucker_size;
622 glScalef (scale, scale, scale * 4);
624 glTranslatef (0, 0, -0.1); /* embed */
626 glTranslatef (1, 0, 0); /* left */
627 draw_sucker (t, front_p);
629 glTranslatef (-2, 0, 0); /* right */
630 draw_sucker (t, front_p);
635 if (texture_p) glEnable (GL_TEXTURE_2D);
639 /* Now draw the end caps.
641 glLineWidth (tc->line_thickness);
642 if (i == 0 || i == t->nsegments-1)
645 GLfloat ctrz = ctr.z + ((i == 0 ? -1 : 1) *
646 t->segments[i].thickness / 4);
649 glColor4fv (t->tentacle_color);
650 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
653 glFrontFace ((front_p ? i == 0 : i != 0) ? GL_CCW : GL_CW);
654 glBegin (wire ? GL_LINES : GL_TRIANGLE_FAN);
655 glNormal3f (0, 0, (i == 0 ? -1 : 1));
656 glTexCoord2f (t0 - 0.25, 0.5);
657 glVertex3f (ctr.x, ctr.y, ctrz);
658 for (j = 0; j <= arg_slices; j++)
660 int jj = j % arg_slices;
661 GLfloat ts = j / (double) arg_slices;
662 glTexCoord2f (t0, ts);
663 glNormal3f (norm[jj].x, norm[jj].y, norm[jj].z);
664 glVertex3f (ring[jj].x, ring[jj].y, ring[jj].z);
665 if (wire) glVertex3f (ctr.x, ctr.y, ctrz);
666 t->mi->polygon_count++;
671 /* Now move to the end of this segment in preparation for the next.
673 if (i != t->nsegments-1)
677 p.y = t->segments[i].length;
684 /* Accumulate the current angle and rotation, to keep track of the
685 rotation of the upcoming segment.
687 cth += t->segments[i].th;
688 cphi += t->segments[i].phi;
692 cphi_sin = sin (cphi);
693 cphi_cos = cos (cphi);
695 memcpy (oring, ring, arg_slices * sizeof(*ring));
696 memcpy (onorm, norm, arg_slices * sizeof(*norm));
713 move_tentacle (tentacle *t)
715 /* tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)]; */
719 int skip = t->nsegments * (1 - (arg_wiggliness + 0.5));
723 for (i = 0; i < t->nsegments; i++)
725 if (++tick >= skip || i == t->nsegments-1)
727 /* randomize the motion of this segment... */
729 double phi_range = M_PI * 0.8 * arg_flexibility;
730 double th_range = M_PI * 0.9 * arg_flexibility;
731 get_position (t->segments[i].rot, &x, &y, &z, True);
732 t->segments[i].phi = phi_range * (0.5 - y);
733 t->segments[i].th = th_range * (0.5 - z);
734 t->segments[i].length = ((0.8 + ((0.5 - x) * 0.4)) *
735 (arg_length / t->nsegments));
737 /* ...and make the previous N segments be interpolated
738 between this one and the previous randomized one. */
739 for (j = last+1; j <= i; j++)
741 t->segments[j].phi = (t->segments[i].phi / (i - last));
742 t->segments[j].th = (t->segments[i].th / (i - last));
743 t->segments[j].length = (t->segments[i].length);
749 len += t->segments[i].length;
752 /* thickness of segment is relative to current position on tentacle
753 (not just the index of the segment). */
754 for (i = 0; i < t->nsegments; i++)
758 double tt = (t->segments[0].thickness * (len - pos) / len);
759 if (tt < 0.001) tt = 0.001;
760 t->segments[i].thickness = tt;
762 pos += t->segments[i].length;
769 tentacles_handle_event (ModeInfo *mi, XEvent *event)
771 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
773 if (event->xany.type == ButtonPress &&
774 event->xbutton.button == Button1)
776 tc->button_down_p = True;
777 gltrackball_start (tc->trackball,
778 event->xbutton.x, event->xbutton.y,
779 MI_WIDTH (mi), MI_HEIGHT (mi));
782 else if (event->xany.type == ButtonRelease &&
783 event->xbutton.button == Button1)
785 tc->button_down_p = False;
788 else if (event->xany.type == ButtonPress &&
789 (event->xbutton.button == Button4 ||
790 event->xbutton.button == Button5 ||
791 event->xbutton.button == Button6 ||
792 event->xbutton.button == Button7))
794 gltrackball_mousewheel (tc->trackball, event->xbutton.button, 10,
795 !!event->xbutton.state);
798 else if (event->xany.type == MotionNotify &&
801 gltrackball_track (tc->trackball,
802 event->xmotion.x, event->xmotion.y,
803 MI_WIDTH (mi), MI_HEIGHT (mi));
806 else if (event->xany.type == KeyPress)
810 XLookupString (&event->xkey, &c, 1, &keysym, 0);
813 gltrackball_reset (tc->trackball);
823 parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
826 a[3] = 1.0; /* alpha */
828 if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
830 fprintf (stderr, "%s: can't parse %s color %s", progname, name, s);
833 a[0] = c.red / 65536.0;
834 a[1] = c.green / 65536.0;
835 a[2] = c.blue / 65536.0;
840 init_tentacles (ModeInfo *mi)
842 tentacles_configuration *tc;
843 int wire = MI_IS_WIREFRAME(mi);
847 tcs = (tentacles_configuration *)
848 calloc (MI_NUM_SCREENS(mi), sizeof (tentacles_configuration));
850 fprintf(stderr, "%s: out of memory\n", progname);
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 ();
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 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
909 if (wire) texture_p = cel_p = False;
910 if (cel_p) texture_p = False;
912 if (texture_p || cel_p) {
913 glGenTextures(1, &tc->texid);
914 # ifdef HAVE_GLBINDTEXTURE
915 glBindTexture ((cel_p ? GL_TEXTURE_1D : GL_TEXTURE_2D), tc->texid);
918 tc->texture = xpm_to_ximage (MI_DISPLAY(mi), MI_VISUAL(mi),
920 (cel_p ? grey_texture : scales));
921 if (!tc->texture) texture_p = cel_p = False;
925 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
927 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
928 tc->texture->width, tc->texture->height, 0,
930 /* GL_UNSIGNED_BYTE, */
931 GL_UNSIGNED_INT_8_8_8_8_REV,
933 check_gl_error("texture");
935 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
936 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
938 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
939 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
941 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
943 glEnable(GL_TEXTURE_2D);
946 glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA,
947 tc->texture->width, 0,
949 /* GL_UNSIGNED_BYTE, */
950 GL_UNSIGNED_INT_8_8_8_8_REV,
952 check_gl_error("texture");
954 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
955 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);
957 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
958 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
960 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
962 glEnable(GL_TEXTURE_1D);
963 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
964 glEnable (GL_LINE_SMOOTH);
965 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
968 /* Dark gray instead of black, so the outlines show up */
969 glClearColor (0.13, 0.13, 0.13, 1.0);
972 compute_unit_torus (mi, 0.5,
973 MAX(5, arg_slices/6),
974 MAX(9, arg_slices/3));
979 draw_tentacles (ModeInfo *mi)
981 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
982 Display *dpy = MI_DISPLAY(mi);
983 Window window = MI_WINDOW(mi);
986 if (!tc->glx_context)
989 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tc->glx_context));
991 glShadeModel(GL_SMOOTH);
993 glEnable(GL_DEPTH_TEST);
994 glEnable(GL_NORMALIZE);
995 glEnable(GL_CULL_FACE);
997 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1000 glRotatef(current_device_rotation(), 0, 0, 1);
1005 glPushAttrib (GL_ENABLE_BIT);
1007 { GLfloat s = 8.7/1600; glScalef(s,s,s); }
1008 glTranslatef(-800,-514,0);
1009 glDisable(GL_LIGHTING);
1010 glDisable(GL_TEXTURE_1D);
1011 glDisable(GL_TEXTURE_2D);
1012 glColor3f (1, 1, 1);
1013 glBegin(GL_LINE_LOOP);
1015 glVertex3f(0,1028,0);
1016 glVertex3f(1600,1028,0);
1017 glVertex3f(1600,0,0);
1023 gltrackball_rotate (tc->trackball);
1025 mi->polygon_count = 0;
1029 glPushAttrib (GL_ENABLE_BIT);
1030 glDisable (GL_LIGHTING);
1031 glDisable (GL_TEXTURE_1D);
1032 glDisable (GL_TEXTURE_2D);
1033 glColor3f (1, 1, 1);
1036 glVertex3f(-0.5, 0, 0); glVertex3f(0.5, 0, 0);
1037 glVertex3f(0, -0.5, 0); glVertex3f(0, 0.5, 0);
1048 glRotatef (ry, 0, 1, 0);
1049 glRotatef (rx, 1, 0, 0);
1050 glRotatef (rz, 0, 0, 1);
1052 glTranslatef (0, -2.0, -4.5);
1054 glTranslatef (0, -2.5, -5.0);
1057 if (!tc->button_down_p)
1058 for (i = 0; i < tc->ntentacles; i++)
1059 move_tentacle (tc->tentacles[i]);
1062 for (i = 0; i < tc->ntentacles; i++)
1065 glClear(GL_DEPTH_BUFFER_BIT);
1066 draw_tentacle (tc->tentacles[i], True);
1068 draw_tentacle (tc->tentacles[i], False);
1074 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1075 draw_sucker (tc->tentacles[0], True);
1078 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1079 glLineWidth (tc->line_thickness);
1080 glColor4fv (tc->outline_color);
1081 draw_sucker (tc->tentacles[0], False);
1087 if (mi->fps_p) do_fps (mi);
1090 glXSwapBuffers(dpy, window);
1093 XSCREENSAVER_MODULE_2 ("SkyTentacles", skytentacles, tentacles)