1 /* Sky Tentacles, Copyright (c) 2008 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);
854 tc = &tcs[MI_SCREEN(mi)];
857 tc = &tcs[MI_SCREEN(mi)];
859 tc->glx_context = init_GL(mi);
861 reshape_tentacles (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
865 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
866 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
867 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
868 glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
869 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
870 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
871 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
876 glEnable (GL_LIGHTING);
877 glEnable (GL_LIGHT0);
880 tc->trackball = gltrackball_init ();
882 tc->left_p = !(random() % 5);
884 if (arg_segments < 2) arg_segments = 2;
885 if (arg_slices < 3) arg_slices = 3;
886 if (arg_thickness < 0.1) arg_thickness = 0.1;
887 if (arg_wiggliness < 0) arg_wiggliness = 0;
888 if (arg_wiggliness > 1) arg_wiggliness = 1;
889 if (arg_flexibility < 0) arg_flexibility = 0;
890 if (arg_flexibility > 1) arg_flexibility = 1;
892 parse_color (mi, "tentacleColor", arg_color, tc->tentacle_color);
893 parse_color (mi, "stripeColor", arg_stripe, tc->stripe_color);
894 parse_color (mi, "suckerColor", arg_sucker, tc->sucker_color);
896 /* Black outlines for light colors, white outlines for dark colors. */
897 if (tc->tentacle_color[0] + tc->tentacle_color[1] + tc->tentacle_color[2]
899 tc->outline_color[0] = 1;
900 tc->outline_color[1] = tc->outline_color[0];
901 tc->outline_color[2] = tc->outline_color[0];
902 tc->outline_color[3] = 1;
904 for (i = 0; i < MI_COUNT(mi); i++)
905 move_tentacle (make_tentacle (mi, i, MI_COUNT(mi)));
907 if (wire) texture_p = cel_p = False;
908 if (cel_p) texture_p = False;
910 if (texture_p || cel_p) {
911 glGenTextures(1, &tc->texid);
912 # ifdef HAVE_GLBINDTEXTURE
913 glBindTexture ((cel_p ? GL_TEXTURE_1D : GL_TEXTURE_2D), tc->texid);
916 tc->texture = xpm_to_ximage (MI_DISPLAY(mi), MI_VISUAL(mi),
918 (cel_p ? grey_texture : scales));
919 if (!tc->texture) texture_p = cel_p = False;
923 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
925 glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
926 tc->texture->width, tc->texture->height, 0,
928 /* GL_UNSIGNED_BYTE, */
929 GL_UNSIGNED_INT_8_8_8_8_REV,
931 check_gl_error("texture");
933 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
934 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
936 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
937 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
939 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
941 glEnable(GL_TEXTURE_2D);
944 glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA,
945 tc->texture->width, 0,
947 /* GL_UNSIGNED_BYTE, */
948 GL_UNSIGNED_INT_8_8_8_8_REV,
950 check_gl_error("texture");
952 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
953 glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);
955 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
956 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
958 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
960 glEnable(GL_TEXTURE_1D);
961 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
962 glEnable (GL_LINE_SMOOTH);
963 glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
966 /* Dark gray instead of black, so the outlines show up */
967 glClearColor (0.08, 0.08, 0.08, 1.0);
970 compute_unit_torus (mi, 0.5,
971 MAX(5, arg_slices/6),
972 MAX(9, arg_slices/3));
977 draw_tentacles (ModeInfo *mi)
979 tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
980 Display *dpy = MI_DISPLAY(mi);
981 Window window = MI_WINDOW(mi);
984 if (!tc->glx_context)
987 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tc->glx_context));
989 glShadeModel(GL_SMOOTH);
991 glEnable(GL_DEPTH_TEST);
992 glEnable(GL_NORMALIZE);
993 glEnable(GL_CULL_FACE);
995 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1003 glPushAttrib (GL_ENABLE_BIT);
1005 { GLfloat s = 8.7/1600; glScalef(s,s,s); }
1006 glTranslatef(-800,-514,0);
1007 glDisable(GL_LIGHTING);
1008 glDisable(GL_TEXTURE_1D);
1009 glDisable(GL_TEXTURE_2D);
1010 glColor3f (1, 1, 1);
1011 glBegin(GL_LINE_LOOP);
1013 glVertex3f(0,1028,0);
1014 glVertex3f(1600,1028,0);
1015 glVertex3f(1600,0,0);
1021 gltrackball_rotate (tc->trackball);
1023 mi->polygon_count = 0;
1027 glPushAttrib (GL_ENABLE_BIT);
1028 glDisable (GL_LIGHTING);
1029 glDisable (GL_TEXTURE_1D);
1030 glDisable (GL_TEXTURE_2D);
1031 glColor3f (1, 1, 1);
1034 glVertex3f(-0.5, 0, 0); glVertex3f(0.5, 0, 0);
1035 glVertex3f(0, -0.5, 0); glVertex3f(0, 0.5, 0);
1046 glRotatef (ry, 0, 1, 0);
1047 glRotatef (rx, 1, 0, 0);
1048 glRotatef (rz, 0, 0, 1);
1050 glTranslatef (0, -2.0, -4.5);
1052 glTranslatef (0, -2.5, -5.0);
1055 if (!tc->button_down_p)
1056 for (i = 0; i < tc->ntentacles; i++)
1057 move_tentacle (tc->tentacles[i]);
1060 for (i = 0; i < tc->ntentacles; i++)
1063 glClear(GL_DEPTH_BUFFER_BIT);
1064 draw_tentacle (tc->tentacles[i], True);
1066 draw_tentacle (tc->tentacles[i], False);
1072 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1073 draw_sucker (tc->tentacles[0], True);
1076 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1077 glLineWidth (tc->line_thickness);
1078 glColor4fv (tc->outline_color);
1079 draw_sucker (tc->tentacles[0], False);
1085 if (mi->fps_p) do_fps (mi);
1088 glXSwapBuffers(dpy, window);
1091 XSCREENSAVER_MODULE_2 ("SkyTentacles", skytentacles, tentacles)