summaryrefslogtreecommitdiffstats
path: root/hacks/glx/skytentacles.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/glx/skytentacles.c')
-rw-r--r--hacks/glx/skytentacles.c1103
1 files changed, 1103 insertions, 0 deletions
diff --git a/hacks/glx/skytentacles.c b/hacks/glx/skytentacles.c
new file mode 100644
index 0000000..1061482
--- /dev/null
+++ b/hacks/glx/skytentacles.c
@@ -0,0 +1,1103 @@
+/* Sky Tentacles, Copyright (c) 2008-2018 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#define DEFAULTS "*delay: 30000 \n" \
+ "*count: 9 \n" \
+ "*showFPS: False \n" \
+ "*wireframe: False \n" \
+
+# define free_tentacles 0
+# define release_tentacles 0
+#undef countof
+#define countof(x) (sizeof((x))/sizeof((*x)))
+
+#include "xlockmore.h"
+#include "colors.h"
+#include "normals.h"
+#include "rotator.h"
+#include "gltrackball.h"
+#include <ctype.h>
+
+#include "ximage-loader.h"
+#include "images/gen/scales_png.h"
+
+#ifdef USE_GL /* whole file */
+
+# ifndef HAVE_JWZGLES
+# define HAVE_POLYGONMODE
+# endif
+
+#define DEF_SPEED "1.0"
+#define DEF_SMOOTH "True"
+#define DEF_TEXTURE "True"
+#define DEF_CEL "False"
+#define DEF_INTERSECT "False"
+#define DEF_SLICES "16"
+#define DEF_SEGMENTS "24"
+#define DEF_WIGGLINESS "0.35"
+#define DEF_FLEXIBILITY "0.35"
+#define DEF_THICKNESS "1.0"
+#define DEF_LENGTH "9.0"
+#define DEF_COLOR "#305A30"
+#define DEF_STRIPE "#451A30"
+#define DEF_SUCKER "#453E30"
+#define DEF_DEBUG "False"
+
+#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
+
+typedef struct {
+ GLfloat length; /* length of the segment coming out of this segment */
+ GLfloat th; /* vector tilt (on yz plane) from previous segment */
+ GLfloat phi; /* vector rotation (on xy plane) from previous segment */
+ GLfloat thickness; /* radius of tentacle at the bottom of this segment */
+ rotator *rot; /* motion modeller */
+} segment;
+
+typedef struct {
+ ModeInfo *mi;
+ GLfloat x, y, z; /* position of the base */
+ int nsegments;
+ segment *segments;
+ GLfloat tentacle_color[4], stripe_color[4], sucker_color[4];
+} tentacle;
+
+typedef struct {
+ GLXContext *glx_context;
+ trackball_state *trackball;
+ Bool button_down_p;
+
+ int ntentacles;
+ int tentacles_size;
+ tentacle **tentacles;
+ GLfloat tentacle_color[4], stripe_color[4], sucker_color[4];
+
+ int torus_polys;
+ int torus_step;
+ XYZ *torus_points;
+ XYZ *torus_normals;
+
+ GLfloat line_thickness;
+ GLfloat outline_color[4];
+ XImage *texture;
+ GLuint texid;
+
+ Bool left_p;
+
+
+} tentacles_configuration;
+
+static tentacles_configuration *tcs = NULL;
+
+static int debug_p;
+static GLfloat arg_speed;
+static int smooth_p;
+static int texture_p;
+static int cel_p;
+static int intersect_p;
+static int arg_slices;
+static int arg_segments;
+static GLfloat arg_thickness;
+static GLfloat arg_length;
+static GLfloat arg_wiggliness;
+static GLfloat arg_flexibility;
+static char *arg_color, *arg_stripe, *arg_sucker;
+
+/* we can only have one light when doing cel shading */
+static GLfloat light_pos[4] = {1.0, 1.0, 1.0, 0.0};
+
+
+static XrmOptionDescRec opts[] = {
+ { "-speed", ".speed", XrmoptionSepArg, 0 },
+ { "-no-smooth", ".smooth", XrmoptionNoArg, "False" },
+ { "-texture", ".texture", XrmoptionNoArg, "True" },
+ { "-no-texture", ".texture", XrmoptionNoArg, "False" },
+ { "-cel", ".cel", XrmoptionNoArg, "True" },
+ { "-no-cel", ".cel", XrmoptionNoArg, "False" },
+ { "-intersect", ".intersect", XrmoptionNoArg, "True" },
+ { "-no-intersect", ".intersect", XrmoptionNoArg, "False" },
+ { "-slices", ".slices", XrmoptionSepArg, 0 },
+ { "-segments", ".segments", XrmoptionSepArg, 0 },
+ { "-thickness", ".thickness", XrmoptionSepArg, 0 },
+ { "-length", ".length", XrmoptionSepArg, 0 },
+ { "-wiggliness", ".wiggliness", XrmoptionSepArg, 0 },
+ { "-flexibility", ".flexibility", XrmoptionSepArg, 0 },
+ { "-color", ".tentacleColor", XrmoptionSepArg, 0 },
+ { "-stripe-color", ".stripeColor", XrmoptionSepArg, 0 },
+ { "-sucker-color", ".suckerColor", XrmoptionSepArg, 0 },
+ { "-debug", ".debug", XrmoptionNoArg, "True" },
+};
+
+static argtype vars[] = {
+ {&arg_speed, "speed", "Speed", DEF_SPEED, t_Float},
+ {&smooth_p, "smooth", "Smooth", DEF_SMOOTH, t_Bool},
+ {&texture_p, "texture", "Texture", DEF_TEXTURE, t_Bool},
+ {&cel_p, "cel", "Cel", DEF_CEL, t_Bool},
+ {&intersect_p, "intersect", "Intersect", DEF_INTERSECT, t_Bool},
+ {&arg_slices, "slices", "Slices", DEF_SLICES, t_Int},
+ {&arg_segments, "segments", "Segments", DEF_SEGMENTS, t_Int},
+ {&arg_thickness, "thickness", "Thickness", DEF_THICKNESS, t_Float},
+ {&arg_length, "length", "Length", DEF_LENGTH, t_Float},
+ {&arg_wiggliness, "wiggliness", "Wiggliness", DEF_WIGGLINESS, t_Float},
+ {&arg_flexibility, "flexibility", "Flexibility", DEF_FLEXIBILITY, t_Float},
+ {&arg_color, "tentacleColor", "Color", DEF_COLOR, t_String},
+ {&arg_stripe, "stripeColor", "Color", DEF_STRIPE, t_String},
+ {&arg_sucker, "suckerColor", "Color", DEF_SUCKER, t_String},
+ {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
+};
+
+ENTRYPOINT ModeSpecOpt tentacles_opts = {countof(opts), opts, countof(vars), vars, NULL};
+
+
+/* Window management, etc
+ */
+ENTRYPOINT void
+reshape_tentacles (ModeInfo *mi, int width, int height)
+{
+ tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
+ GLfloat h = (GLfloat) height / (GLfloat) width;
+ int y = 0;
+
+ if (width > height * 5) { /* tiny window: show middle */
+ height = width * 9/16;
+ y = -height/2;
+ h = height / (GLfloat) width;
+ }
+
+ glViewport (0, y, (GLint) width, (GLint) height);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluPerspective (30.0, 1/h, 1.0, 100.0);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ gluLookAt( 0.0, 0.0, 30.0,
+ 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ tc->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (3, width / 200));
+}
+
+
+static void
+normalize (GLfloat *x, GLfloat *y, GLfloat *z)
+{
+ GLfloat d = sqrt((*x)*(*x) + (*y)*(*y) + (*z)*(*z));
+ *x /= d;
+ *y /= d;
+ *z /= d;
+}
+
+static GLfloat
+dot (GLfloat x0, GLfloat y0, GLfloat z0,
+ GLfloat x1, GLfloat y1, GLfloat z1)
+{
+ return x0*x1 + y0*y1 + z0*z1;
+}
+
+
+static void
+compute_unit_torus (ModeInfo *mi, double ratio, int slices1, int slices2)
+{
+ tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
+ Bool wire = MI_IS_WIREFRAME (mi);
+ int i, j, k, fp;
+
+ if (wire) slices1 /= 2;
+ if (wire) slices2 /= 4;
+ if (slices1 < 3) slices1 = 3;
+ if (slices2 < 3) slices2 = 3;
+
+ tc->torus_polys = slices1 * (slices2+1) * 2;
+ tc->torus_points = (XYZ *) calloc (tc->torus_polys + 1,
+ sizeof (*tc->torus_points));
+ tc->torus_normals = (XYZ *) calloc (tc->torus_polys + 1,
+ sizeof (*tc->torus_normals));
+ tc->torus_step = 2 * (slices2+1);
+ fp = 0;
+ for (i = 0; i < slices1; i++)
+ for (j = 0; j <= slices2; j++)
+ for (k = 0; k <= 1; k++)
+ {
+ double s = (i + k) % slices1 + 0.5;
+ double t = j % slices2;
+ XYZ p;
+ p.x = cos(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
+ p.y = sin(t*M_PI*2/slices2) * cos(s*M_PI*2/slices1);
+ p.z = sin(s*M_PI*2/slices1);
+ tc->torus_normals[fp] = p;
+
+ p.x = (1 + ratio * cos(s*M_PI*2/slices1)) * cos(t*M_PI*2/slices2) / 2;
+ p.y = (1 + ratio * cos(s*M_PI*2/slices1)) * sin(t*M_PI*2/slices2) / 2;
+ p.z = ratio * sin(s*M_PI*2/slices1) / 2;
+ tc->torus_points[fp] = p;
+ fp++;
+ }
+ if (fp != tc->torus_polys) abort();
+ tc->torus_polys = fp;
+}
+
+
+/* Initializes a new tentacle and stores it in the list.
+ */
+static tentacle *
+make_tentacle (ModeInfo *mi, int which, int total)
+{
+ tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
+ tentacle *t = (tentacle *) calloc (1, sizeof (*t));
+ double brightness;
+ int i;
+
+ t->mi = mi;
+
+ /* position tentacles on a grid */
+ {
+ int cols = (int) (sqrt(total) + 0.5);
+ int rows = (total+cols-1) / cols;
+ int xx = which % cols;
+ int yy = which / cols;
+ double spc = arg_thickness * 0.8;
+ if (!intersect_p) cols = 1, xx = 0;
+ t->x = (cols * spc / 2) - (spc * (xx + 0.5));
+ t->y = (rows * spc / 2) - (spc * (yy + 0.5));
+ t->z = 0;
+ }
+
+ /* Brighten or darken the colors of this tentacle from the default.
+ */
+ brightness = 0.6 + frand(3.0);
+ memcpy (t->tentacle_color, tc->tentacle_color, 4 * sizeof(*t->tentacle_color));
+ memcpy (t->stripe_color, tc->stripe_color, 4 * sizeof(*t->stripe_color));
+ memcpy (t->sucker_color, tc->sucker_color, 4 * sizeof(*t->sucker_color));
+# define FROB(X) \
+ t->X[0] *= brightness; if (t->X[0] > 1) t->X[0] = 1; \
+ t->X[1] *= brightness; if (t->X[1] > 1) t->X[1] = 1; \
+ t->X[2] *= brightness; if (t->X[2] > 1) t->X[2] = 1
+ FROB (tentacle_color);
+ FROB (stripe_color);
+ FROB (sucker_color);
+# undef FROB
+
+ t->nsegments = (arg_segments) + BELLRAND(arg_segments);
+
+ t->segments = (segment *) calloc (t->nsegments+1, sizeof(*t->segments));
+ for (i = 0; i < t->nsegments; i++)
+ {
+ double spin_speed = 0;
+ double spin_accel = 0;
+ double wander_speed = arg_speed * (0.02 + BELLRAND(0.1));
+ t->segments[i].rot = make_rotator (spin_speed, spin_speed, spin_speed,
+ spin_accel, wander_speed, True);
+ }
+
+ t->segments[0].thickness = (((arg_thickness * 0.5) +
+ BELLRAND(arg_thickness * 0.6))
+ / 1.0);
+
+ if (tc->tentacles_size <= tc->ntentacles)
+ {
+ tc->tentacles_size = (tc->tentacles_size * 1.2) + tc->ntentacles + 2;
+ tc->tentacles = (tentacle **)
+ realloc (tc->tentacles, tc->tentacles_size * sizeof(*tc->tentacles));
+ if (! tc->tentacles)
+ {
+ fprintf (stderr, "%s: out of memory (%d tentacles)\n",
+ progname, tc->tentacles_size);
+ exit (1);
+ }
+ }
+
+ tc->tentacles[tc->ntentacles++] = t;
+ return t;
+}
+
+
+static void
+draw_sucker (tentacle *t, Bool front_p)
+{
+ tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)];
+ Bool wire = MI_IS_WIREFRAME (t->mi);
+ int i, j;
+ int strips = tc->torus_polys / tc->torus_step;
+ int points = 0;
+
+ glFrontFace (front_p ? GL_CW : GL_CCW);
+ for (i = 0; i < strips; i++)
+ {
+ int ii = i * tc->torus_step;
+
+ /* Leave off the polygons on the underside. This reduces polygon
+ count by about 10% with the default settings. */
+ if (strips > 4 && i >= strips/2 && i < strips-1)
+ continue;
+
+ glBegin (wire ? GL_LINE_STRIP : GL_QUAD_STRIP);
+ for (j = 0; j < tc->torus_step; j++)
+ {
+ XYZ sp = tc->torus_points[ii+j];
+ XYZ sn = tc->torus_normals[ii+j];
+ glNormal3f(sn.x, sn.y, sn.z);
+ glVertex3f(sp.x, sp.y, sp.z);
+ points++;
+ }
+ glEnd();
+ }
+ t->mi->polygon_count += points/2;
+}
+
+static void
+draw_tentacle_1 (tentacle *t, Bool front_p, Bool outline_p)
+{
+ tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)];
+ int i;
+ Bool wire = MI_IS_WIREFRAME (t->mi);
+ XYZ ctr = { 0,0,0 }; /* current position of base of segment */
+ double cth = 0; /* overall orientation of current segment */
+ double cphi = 0;
+ double cth_cos = 1, cth_sin = 0;
+ double cphi_cos = 1, cphi_sin = 0;
+
+ GLfloat light[3]; /* vector to the light */
+
+ GLfloat t0 = 0.0; /* texture coordinate */
+
+ XYZ *ring, *oring; /* points around the edge (this, and previous) */
+ XYZ *norm, *onorm; /* their normals */
+ XYZ *ucirc; /* unit circle, to save some trig */
+
+ /* Which portion of the radius the indented/colored stripe takes up */
+ int indented_points = arg_slices * 0.2;
+
+ /* We do rotation this way to minimize the number of calls to sin/cos.
+ We have to hack the transformations manually instead of using
+ glRotate/glTranslate because those calls are not allowed *inside*
+ of a glBegin/glEnd block...
+ */
+# define ROT(P) do { \
+ XYZ _p = P; \
+ _p.y = ((P.y * cth_sin - P.x * cth_cos)); \
+ _p.x = ((P.y * cth_cos + P.x * cth_sin) * cphi_sin - (P.z * cphi_cos)); \
+ _p.z = ((P.y * cth_cos + P.x * cth_sin) * cphi_cos + (P.z * cphi_sin)); \
+ P = _p; \
+ } while(0)
+
+ ring = (XYZ *) malloc (arg_slices * sizeof(*ring));
+ norm = (XYZ *) malloc (arg_slices * sizeof(*norm));
+ oring = (XYZ *) malloc (arg_slices * sizeof(*oring));
+ onorm = (XYZ *) malloc (arg_slices * sizeof(*onorm));
+ ucirc = (XYZ *) malloc (arg_slices * sizeof(*ucirc));
+
+ light[0] = light_pos[0];
+ light[1] = light_pos[1];
+ light[2] = light_pos[2];
+ normalize (&light[0], &light[1], &light[2]);
+
+ for (i = 0; i < arg_slices; i++)
+ {
+ double a = M_PI * 2 * i / arg_slices;
+ ucirc[i].x = cos(a);
+ ucirc[i].y = sin(a);
+ ucirc[i].z = 0;
+ }
+
+
+# ifdef HAVE_POLYGONMODE
+ if (cel_p)
+ glPolygonMode (GL_FRONT_AND_BACK, (front_p ? GL_FILL : GL_LINE));
+# endif
+
+ glPushMatrix();
+ glTranslatef (t->x, t->y, t->z);
+
+ if (debug_p)
+ {
+ glPushAttrib (GL_ENABLE_BIT);
+ glDisable (GL_LIGHTING);
+ glDisable (GL_TEXTURE_1D);
+ glDisable (GL_TEXTURE_2D);
+ glColor3f (1, 1, 1);
+ glLineWidth (1);
+ glBegin(GL_LINE_LOOP);
+ for (i = 0; i < arg_slices; i++)
+ glVertex3f (arg_thickness / 2 * cos (M_PI * 2 * i / arg_slices),
+ arg_thickness / 2 * sin (M_PI * 2 * i / arg_slices),
+ 0);
+ glEnd();
+ glPopAttrib();
+ }
+
+ if (!front_p || outline_p)
+ glColor4fv (tc->outline_color);
+ else if (wire)
+ glColor4fv (t->tentacle_color);
+ else
+ {
+ static const GLfloat bspec[4] = {1.0, 1.0, 1.0, 1.0};
+ static const GLfloat bshiny = 128.0;
+ glMaterialfv (GL_FRONT, GL_SPECULAR, bspec);
+ glMateriali (GL_FRONT, GL_SHININESS, bshiny);
+ }
+
+ for (i = 0; i < t->nsegments; i++)
+ {
+ int j;
+ GLfloat t1 = t0 + i / (t->nsegments * M_PI * 2);
+
+ for (j = 0; j < arg_slices; j++)
+ {
+ /* Construct a vertical disc at the origin, to use as the
+ base of this segment.
+ */
+ double r = t->segments[i].thickness / 2;
+
+ if (j <= indented_points/2 || j >= arg_slices-indented_points/2)
+ r *= 0.75; /* indent the stripe */
+
+ if (outline_p)
+ r *= 1.1;
+
+ ring[j].x = r * ucirc[j].x;
+ ring[j].y = 0;
+ ring[j].z = r * ucirc[j].y;
+
+ /* Then rotate the points by the angle of the current segment. */
+ ROT(ring[j]);
+
+ /* Then move the ring to the base of this segment. */
+ ring[j].x += ctr.x;
+ ring[j].y += ctr.y;
+ ring[j].z += ctr.z;
+ }
+
+
+ /* Compute the normals of the faces on this segment. We do this
+ first so that the normals of the vertexes can be the average
+ of the normals of the faces.
+ #### Uh, except I didn't actually implement that...
+ but it would be a good idea.
+ */
+ if (i > 0)
+ for (j = 0; j <= arg_slices; j++)
+ {
+ int j0 = j % arg_slices;
+ int j1 = (j+1) % arg_slices;
+ norm[j0] = calc_normal (oring[j0], ring[j0], ring[j1]);
+ }
+
+ /* Draw!
+ */
+ if (i > 0)
+ {
+ int j;
+ glLineWidth (tc->line_thickness);
+ glFrontFace (front_p ? GL_CCW : GL_CW);
+ glBegin (wire ? GL_LINES : smooth_p ? GL_QUAD_STRIP : GL_QUADS);
+ for (j = 0; j <= arg_slices; j++)
+ {
+ int j0 = j % arg_slices;
+ int j1 = (j+1) % arg_slices;
+
+ GLfloat ts = j / (double) arg_slices;
+
+ if (!front_p || outline_p)
+ glColor4fv (tc->outline_color);
+ else if (j <= indented_points/2 ||
+ j >= arg_slices-indented_points/2)
+ {
+ glColor4fv (t->stripe_color);
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
+ t->stripe_color);
+ }
+ else
+ {
+ glColor4fv (t->tentacle_color);
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
+ t->tentacle_color);
+ }
+
+ /* For cel shading, the 1d texture coordinate (s) is the
+ dot product of the lighting vector and the vertex normal.
+ */
+ if (cel_p)
+ {
+ t0 = dot (light[0], light[1], light[2],
+ onorm[j0].x, onorm[j0].y, onorm[j0].z);
+ t1 = dot (light[0], light[1], light[2],
+ norm[j0].x, norm[j0].y, norm[j0].z);
+ if (t0 < 0) t0 = 0;
+ if (t1 < 0) t1 = 0;
+ }
+
+ glTexCoord2f (t0, ts);
+ glNormal3f (onorm[j0].x, onorm[j0].y, onorm[j0].z);
+ glVertex3f (oring[j0].x, oring[j0].y, oring[j0].z);
+
+ glTexCoord2f (t1, ts);
+ glNormal3f ( norm[j0].x, norm[j0].y, norm[j0].z);
+ glVertex3f ( ring[j0].x, ring[j0].y, ring[j0].z);
+
+ if (!smooth_p)
+ {
+ ts = j1 / (double) arg_slices;
+ glTexCoord2f (t1, ts);
+ glVertex3f ( ring[j1].x, ring[j1].y, ring[j1].z);
+ glTexCoord2f (t0, ts);
+ glVertex3f (oring[j1].x, oring[j1].y, oring[j1].z);
+ }
+ t->mi->polygon_count++;
+ }
+ glEnd ();
+
+ if (wire)
+ {
+ glBegin (GL_LINE_LOOP);
+ for (j = 0; j < arg_slices; j++)
+ glVertex3f (ring[j].x, ring[j].y, ring[j].z);
+ glEnd();
+ }
+
+ /* Now draw the suckers!
+ */
+ {
+ double seg_length = arg_length / t->nsegments;
+ double sucker_size = arg_thickness / 5;
+ double sucker_spacing = sucker_size * 1.3;
+ int nsuckers = seg_length / sucker_spacing;
+ double oth = cth - t->segments[i-1].th;
+ double ophi = cphi - t->segments[i-1].phi;
+ int k;
+
+ if (!wire)
+ glLineWidth (MAX (2, tc->line_thickness / 2.0));
+ glDisable (GL_TEXTURE_2D);
+
+ /* Sometimes we have N suckers on one segment;
+ sometimes we have one sucker every N segments. */
+ if (nsuckers == 0)
+ {
+ int segs_per_sucker =
+ (int) ((sucker_spacing / seg_length) + 0.5);
+ nsuckers = (i % segs_per_sucker) ? 0 : 1;
+ }
+
+ if (outline_p)
+ {
+ glColor4fv (tc->outline_color);
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
+ tc->outline_color);
+ }
+ else if (front_p)
+ {
+ glColor4fv (t->sucker_color);
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
+ t->sucker_color);
+ }
+
+ for (k = 0; k < nsuckers; k++)
+ {
+ double scale;
+ XYZ p0 = ring[0];
+ XYZ p1 = oring[0];
+ XYZ p;
+ p.x = p0.x + (p1.x - p0.x) * (k + 0.5) / nsuckers;
+ p.y = p0.y + (p1.y - p0.y) * (k + 0.5) / nsuckers;
+ p.z = p0.z + (p1.z - p0.z) * (k + 0.5) / nsuckers;
+
+ glPushMatrix();
+ glTranslatef (p.x, p.y, p.z);
+ glRotatef (ophi * 180 / M_PI, 0, 1, 0);
+ glRotatef (-oth * 180 / M_PI, 1, 0, 0);
+ glRotatef (90, 1, 0, 0);
+
+ { /* Not quite right: this is the slope of the outer edge
+ if the next segment was not tilted at all... If there
+ is any tilt, then the angle of this wall and the
+ opposite wall are very different.
+ */
+ double slope = ((t->segments[i-1].thickness -
+ t->segments[i].thickness) /
+ t->segments[i].length);
+ glRotatef (-45 * slope, 1, 0, 0);
+ }
+
+ scale = t->segments[i].thickness / arg_thickness;
+ scale *= 0.7 * sucker_size;
+
+ glScalef (scale, scale, scale * 4);
+
+ glTranslatef (0, 0, -0.1); /* embed */
+
+ if (outline_p)
+ {
+ scale = 1.1;
+ glScalef (scale, scale, scale);
+ }
+
+ glTranslatef (1, 0, 0); /* left */
+ draw_sucker (t, front_p);
+
+ glTranslatef (-2, 0, 0); /* right */
+ draw_sucker (t, front_p);
+
+ glPopMatrix();
+ }
+
+ if (texture_p) glEnable (GL_TEXTURE_2D);
+ }
+ }
+
+ /* Now draw the end caps.
+ */
+ glLineWidth (tc->line_thickness);
+ if (!outline_p && (i == 0 || i == t->nsegments-1))
+ {
+ int j;
+ GLfloat ctrz = ctr.z + ((i == 0 ? -1 : 1) *
+ t->segments[i].thickness / 4);
+ if (front_p)
+ {
+ glColor4fv (t->tentacle_color);
+ glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
+ t->tentacle_color);
+ }
+ glFrontFace ((front_p ? i == 0 : i != 0) ? GL_CCW : GL_CW);
+ glBegin (wire ? GL_LINES : GL_TRIANGLE_FAN);
+ glNormal3f (0, 0, (i == 0 ? -1 : 1));
+ glTexCoord2f (t0 - 0.25, 0.5);
+ glVertex3f (ctr.x, ctr.y, ctrz);
+ for (j = 0; j <= arg_slices; j++)
+ {
+ int jj = j % arg_slices;
+ GLfloat ts = j / (double) arg_slices;
+ glTexCoord2f (t0, ts);
+ glNormal3f (norm[jj].x, norm[jj].y, norm[jj].z);
+ glVertex3f (ring[jj].x, ring[jj].y, ring[jj].z);
+ if (wire) glVertex3f (ctr.x, ctr.y, ctrz);
+ t->mi->polygon_count++;
+ }
+ glEnd();
+ }
+
+ /* Now move to the end of this segment in preparation for the next.
+ */
+ if (i != t->nsegments-1)
+ {
+ XYZ p;
+ p.x = 0;
+ p.y = t->segments[i].length;
+ p.z = 0;
+ ROT (p);
+ ctr.x += p.x;
+ ctr.y += p.y;
+ ctr.z += p.z;
+
+ /* Accumulate the current angle and rotation, to keep track of the
+ rotation of the upcoming segment.
+ */
+ cth += t->segments[i].th;
+ cphi += t->segments[i].phi;
+
+ cth_sin = sin (cth);
+ cth_cos = cos (cth);
+ cphi_sin = sin (cphi);
+ cphi_cos = cos (cphi);
+
+ memcpy (oring, ring, arg_slices * sizeof(*ring));
+ memcpy (onorm, norm, arg_slices * sizeof(*norm));
+ }
+
+ t0 = t1;
+ }
+
+ glPopMatrix();
+
+ free (ring);
+ free (norm);
+ free (oring);
+ free (onorm);
+ free (ucirc);
+}
+
+
+static void
+draw_tentacle (tentacle *t, Bool front_p)
+{
+# ifndef HAVE_POLYGONMODE
+ Bool wire = MI_IS_WIREFRAME (t->mi);
+ if (!wire && cel_p && front_p)
+ {
+ draw_tentacle_1 (t, front_p, True);
+ glClear (GL_DEPTH_BUFFER_BIT);
+ }
+# endif /* HAVE_POLYGONMODE */
+
+ draw_tentacle_1 (t, front_p, False);
+}
+
+
+static void
+move_tentacle (tentacle *t)
+{
+ /* tentacles_configuration *tc = &tcs[MI_SCREEN(t->mi)]; */
+ GLfloat len = 0;
+ double pos = 0;
+ int i, j;
+ int skip = t->nsegments * (1 - (arg_wiggliness + 0.5));
+ int tick = 0;
+ int last = 0;
+
+ for (i = 0; i < t->nsegments; i++)
+ {
+ if (++tick >= skip || i == t->nsegments-1)
+ {
+ /* randomize the motion of this segment... */
+ double x, y, z;
+ double phi_range = M_PI * 0.8 * arg_flexibility;
+ double th_range = M_PI * 0.9 * arg_flexibility;
+ get_position (t->segments[i].rot, &x, &y, &z, True);
+ t->segments[i].phi = phi_range * (0.5 - y);
+ t->segments[i].th = th_range * (0.5 - z);
+ t->segments[i].length = ((0.8 + ((0.5 - x) * 0.4)) *
+ (arg_length / t->nsegments));
+
+ /* ...and make the previous N segments be interpolated
+ between this one and the previous randomized one. */
+ for (j = last+1; j <= i; j++)
+ {
+ t->segments[j].phi = (t->segments[i].phi / (i - last));
+ t->segments[j].th = (t->segments[i].th / (i - last));
+ t->segments[j].length = (t->segments[i].length);
+ }
+
+ tick = 0;
+ last = i;
+ }
+ len += t->segments[i].length;
+ }
+
+ /* thickness of segment is relative to current position on tentacle
+ (not just the index of the segment). */
+ for (i = 0; i < t->nsegments; i++)
+ {
+ if (i > 0)
+ {
+ double tt = (t->segments[0].thickness * (len - pos) / len);
+ if (tt < 0.001) tt = 0.001;
+ t->segments[i].thickness = tt;
+ }
+ pos += t->segments[i].length;
+ }
+}
+
+
+
+ENTRYPOINT Bool
+tentacles_handle_event (ModeInfo *mi, XEvent *event)
+{
+ tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
+
+ if (gltrackball_event_handler (event, tc->trackball,
+ MI_WIDTH (mi), MI_HEIGHT (mi),
+ &tc->button_down_p))
+ return True;
+ else if (event->xany.type == KeyPress)
+ {
+ KeySym keysym;
+ char c = 0;
+ XLookupString (&event->xkey, &c, 1, &keysym, 0);
+ if (c == ' ')
+ {
+ gltrackball_reset (tc->trackball, 0, 0);
+ return True;
+ }
+ }
+
+ return False;
+}
+
+
+static void
+parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
+{
+ XColor c;
+ a[3] = 1.0; /* alpha */
+
+ if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
+ {
+ fprintf (stderr, "%s: can't parse %s color %s", progname, name, s);
+ exit (1);
+ }
+ a[0] = c.red / 65536.0;
+ a[1] = c.green / 65536.0;
+ a[2] = c.blue / 65536.0;
+}
+
+
+ENTRYPOINT void
+init_tentacles (ModeInfo *mi)
+{
+ tentacles_configuration *tc;
+ int wire = MI_IS_WIREFRAME(mi);
+ int i;
+
+ MI_INIT (mi, tcs);
+
+ tc = &tcs[MI_SCREEN(mi)];
+
+ tc->glx_context = init_GL(mi);
+
+ reshape_tentacles (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
+
+ if (!wire)
+ {
+ GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
+ GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
+ GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
+ glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
+ glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
+ glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
+ }
+
+ if (!wire && !cel_p)
+ {
+ glEnable (GL_LIGHTING);
+ glEnable (GL_LIGHT0);
+ }
+
+ tc->trackball = gltrackball_init (False);
+
+ tc->left_p = !(random() % 5);
+
+ if (arg_segments < 2) arg_segments = 2;
+ if (arg_slices < 3) arg_slices = 3;
+ if (arg_thickness < 0.1) arg_thickness = 0.1;
+ if (arg_wiggliness < 0) arg_wiggliness = 0;
+ if (arg_wiggliness > 1) arg_wiggliness = 1;
+ if (arg_flexibility < 0) arg_flexibility = 0;
+ if (arg_flexibility > 1) arg_flexibility = 1;
+
+ parse_color (mi, "tentacleColor", arg_color, tc->tentacle_color);
+ parse_color (mi, "stripeColor", arg_stripe, tc->stripe_color);
+ parse_color (mi, "suckerColor", arg_sucker, tc->sucker_color);
+
+ /* Black outlines for light colors, white outlines for dark colors. */
+ if (tc->tentacle_color[0] + tc->tentacle_color[1] + tc->tentacle_color[2]
+ < 0.4)
+ tc->outline_color[0] = 1;
+ tc->outline_color[1] = tc->outline_color[0];
+ tc->outline_color[2] = tc->outline_color[0];
+ tc->outline_color[3] = 1;
+
+ for (i = 0; i < MI_COUNT(mi); i++)
+ move_tentacle (make_tentacle (mi, i, MI_COUNT(mi)));
+
+ if (wire) texture_p = cel_p = False;
+ if (cel_p) texture_p = False;
+
+ if (texture_p || cel_p) {
+ glGenTextures(1, &tc->texid);
+# ifdef HAVE_GLBINDTEXTURE
+ glBindTexture ((cel_p ? GL_TEXTURE_1D : GL_TEXTURE_2D), tc->texid);
+# endif
+
+ if (cel_p)
+ {
+ /* "16 1 3 1",
+ "X c #808080",
+ "x c #C0C0C0",
+ ". c #FFFFFF",
+ "XXXxxxxx........"
+ */
+ int w = 16;
+ tc->texture = XCreateImage (MI_DISPLAY(mi), MI_VISUAL(mi),
+ 32, ZPixmap, 0, 0, w, 1, 32, 0);
+ tc->texture->data = (char *) calloc(1, tc->texture->bytes_per_line);
+ /* ABGR */
+ for (i = 0; i < 3; i++) XPutPixel (tc->texture, i, 0, 0xFF808080);
+ for (; i < 8; i++) XPutPixel (tc->texture, i, 0, 0xFFC0C0C0);
+ for (; i < w; i++) XPutPixel (tc->texture, i, 0, 0xFFFFFFFF);
+ }
+ else
+ tc->texture = image_data_to_ximage (MI_DISPLAY(mi), MI_VISUAL(mi),
+ scales_png, sizeof(scales_png));
+
+ if (!tc->texture) texture_p = cel_p = False;
+ }
+
+ if (texture_p) {
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+ clear_gl_error();
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
+ tc->texture->width, tc->texture->height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, tc->texture->data);
+ check_gl_error("texture");
+
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ glEnable(GL_TEXTURE_2D);
+ } else if (cel_p) {
+ clear_gl_error();
+ glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA,
+ tc->texture->width, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, tc->texture->data);
+ check_gl_error("texture");
+
+ glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ glEnable(GL_TEXTURE_1D);
+ glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
+ glEnable (GL_LINE_SMOOTH);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_BLEND);
+
+ /* Dark gray instead of black, so the outlines show up */
+ glClearColor (0.13, 0.13, 0.13, 1.0);
+ }
+
+ compute_unit_torus (mi, 0.5,
+ MAX(5, arg_slices/6),
+ MAX(9, arg_slices/3));
+}
+
+
+ENTRYPOINT void
+draw_tentacles (ModeInfo *mi)
+{
+ tentacles_configuration *tc = &tcs[MI_SCREEN(mi)];
+ Display *dpy = MI_DISPLAY(mi);
+ Window window = MI_WINDOW(mi);
+ int i;
+
+ if (!tc->glx_context)
+ return;
+
+ glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(tc->glx_context));
+
+ glShadeModel(GL_SMOOTH);
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_NORMALIZE);
+ glEnable(GL_CULL_FACE);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ glPushMatrix ();
+ glRotatef(current_device_rotation(), 0, 0, 1);
+
+# if 1
+ glScalef (3, 3, 3);
+# else
+ glPushAttrib (GL_ENABLE_BIT);
+ glPushMatrix();
+ { GLfloat s = 8.7/1600; glScalef(s,s,s); }
+ glTranslatef(-800,-514,0);
+ glDisable(GL_LIGHTING);
+ glDisable(GL_TEXTURE_1D);
+ glDisable(GL_TEXTURE_2D);
+ glColor3f (1, 1, 1);
+ glBegin(GL_LINE_LOOP);
+ glVertex3f(0,0,0);
+ glVertex3f(0,1028,0);
+ glVertex3f(1600,1028,0);
+ glVertex3f(1600,0,0);
+ glEnd();
+ glPopMatrix();
+ glPopAttrib();
+# endif
+
+ gltrackball_rotate (tc->trackball);
+
+ mi->polygon_count = 0;
+
+ if (debug_p)
+ {
+ glPushAttrib (GL_ENABLE_BIT);
+ glDisable (GL_LIGHTING);
+ glDisable (GL_TEXTURE_1D);
+ glDisable (GL_TEXTURE_2D);
+ glColor3f (1, 1, 1);
+ glLineWidth (1);
+ glBegin(GL_LINES);
+ glVertex3f(-0.5, 0, 0); glVertex3f(0.5, 0, 0);
+ glVertex3f(0, -0.5, 0); glVertex3f(0, 0.5, 0);
+ glEnd();
+ glPopAttrib();
+ }
+ else
+ {
+ GLfloat rx = 45;
+ GLfloat ry = -45;
+ GLfloat rz = 70;
+ if (tc->left_p)
+ ry = -ry, rz = -rz;
+ glRotatef (ry, 0, 1, 0);
+ glRotatef (rx, 1, 0, 0);
+ glRotatef (rz, 0, 0, 1);
+ if (intersect_p)
+ glTranslatef (0, -2.0, -4.5);
+ else
+ glTranslatef (0, -2.5, -5.0);
+ }
+
+ if (!tc->button_down_p)
+ for (i = 0; i < tc->ntentacles; i++)
+ move_tentacle (tc->tentacles[i]);
+
+#if 1
+ for (i = 0; i < tc->ntentacles; i++)
+ {
+ if (! intersect_p)
+ glClear(GL_DEPTH_BUFFER_BIT);
+ draw_tentacle (tc->tentacles[i], True);
+ if (cel_p)
+ draw_tentacle (tc->tentacles[i], False);
+ }
+#else
+ glScalef (3, 3, 3);
+ glScalef (1, 1, 4);
+ glColor3f(1,1,1);
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+ draw_sucker (tc->tentacles[0], True);
+ if (cel_p)
+ {
+ glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+ glLineWidth (tc->line_thickness);
+ glColor4fv (tc->outline_color);
+ draw_sucker (tc->tentacles[0], False);
+ }
+#endif
+
+ glPopMatrix ();
+
+ if (mi->fps_p) do_fps (mi);
+ glFinish();
+
+ glXSwapBuffers(dpy, window);
+}
+
+XSCREENSAVER_MODULE_2 ("SkyTentacles", skytentacles, tentacles)
+
+#endif /* USE_GL */