summaryrefslogtreecommitdiffstats
path: root/hacks/glx/jigsaw.c
diff options
context:
space:
mode:
Diffstat (limited to 'hacks/glx/jigsaw.c')
-rw-r--r--hacks/glx/jigsaw.c1511
1 files changed, 0 insertions, 1511 deletions
diff --git a/hacks/glx/jigsaw.c b/hacks/glx/jigsaw.c
deleted file mode 100644
index 206c346..0000000
--- a/hacks/glx/jigsaw.c
+++ /dev/null
@@ -1,1511 +0,0 @@
-/* xscreensaver, Copyright (c) 1997-2019 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.
- *
- * Written as an Xlib program some time in 1997.
- * Rewritten as an OpenGL program 24-Aug-2008.
- */
-
-/*
- Currently, we do this:
-
- - start pieces off screen and move them in.
- - when they land, they fill the puzzle grid with a shuffled
- puzzle (pieces are rotated, too).
- - swap random pairs of pieces until the puzzle is solved.
- - scatter the pieces off screen (resulting in black).
- - load new image and repeat.
-
- Another idea would be to show the puzzle being solved the way
- a person would do it:
-
- - start off with black screen.
- - assume knowledge of size of grid (position of corners).
- - find a corner piece, and place it.
- - while puzzle unsolved:
- - pick a random piece;
- - if it is the correct piece for any open edge, place it;
- - if it fits physically in any rotation at any open edge,
- place it, then toss it back (show the fake-out).
- - if it doesn't fit at all, don't animate it at all.
-
- This would take a long time to solve, I think...
-
- An even harder idea would involve building up completed "clumps"
- and sliding them around (a coral growth / accretion approach)
- */
-
-#define DEF_SPEED "1.0"
-#define DEF_COMPLEXITY "1.0"
-#define DEF_RESOLUTION "100"
-#define DEF_THICKNESS "0.06"
-#define DEF_WOBBLE "True"
-#define DEF_DEBUG "False"
-
-#define DEF_FONT "sans-serif bold 24"
-#define DEFAULTS "*delay: 20000 \n" \
- "*showFPS: False \n" \
- "*font: " DEF_FONT"\n" \
- "*wireframe: False \n" \
- "*desktopGrabber: xscreensaver-getimage -no-desktop %s\n" \
- "*grabDesktopImages: False \n" \
- "*chooseRandomImages: True \n" \
- "*suppressRotationAnimation: True\n" \
-
-
-# define release_jigsaw 0
-
-#include "xlockmore.h"
-#include "rotator.h"
-#include "gltrackball.h"
-#include "spline.h"
-#include "normals.h"
-#include "grab-ximage.h"
-#include "texfont.h"
-
-#ifndef HAVE_JWZGLES
-# define HAVE_TESS
-#endif /* !HAVE_JWZGLES */
-
-#undef BELLRAND
-#define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
-
-#ifdef USE_GL /* whole file */
-
-#define TOP 0
-#define RIGHT 1
-#define BOTTOM 2
-#define LEFT 3
-
-#define IN -1
-#define FLAT 0
-#define OUT 1
-
-typedef struct jigsaw_configuration jigsaw_configuration;
-
-typedef struct {
- double x,y,z,r; /* position and Z rotation (in degrees) */
-} XYZR;
-
-
-typedef struct {
- jigsaw_configuration *jc;
- int edge[4];
- GLuint dlist;
- int polys;
-
- XYZR home; /* correct position in puzzle */
- XYZR current; /* where it is right now */
- XYZR from, to; /* in transit from A to B */
- double tick; /* 0-1.0, how far from A to B */
- double arc_height; /* height of apex of curved path from A to B */
- double tilt, max_tilt;
-
-} puzzle_piece;
-
-
-struct jigsaw_configuration {
- GLXContext *glx_context;
- trackball_state *trackball;
- rotator *rot;
- Bool button_down_p;
- texture_font_data *texfont;
- GLuint loading_dlist;
-
- int puzzle_width;
- int puzzle_height;
- puzzle_piece *puzzle;
-
- enum { PUZZLE_LOADING_MSG,
- PUZZLE_LOADING,
- PUZZLE_UNSCATTER,
- PUZZLE_SOLVE,
- PUZZLE_SCATTER } state;
- double pausing;
- double tick_speed;
-
- GLuint texid;
- GLfloat tex_x, tex_y, tex_width, tex_height, aspect;
-
- GLuint line_thickness;
-};
-
-static jigsaw_configuration *sps = NULL;
-
-static GLfloat speed;
-static GLfloat complexity_arg;
-static int resolution_arg;
-static GLfloat thickness_arg;
-static Bool wobble_p;
-static Bool debug_p;
-
-static XrmOptionDescRec opts[] = {
- { "-speed", ".speed", XrmoptionSepArg, 0 },
- { "-complexity", ".complexity", XrmoptionSepArg, 0 },
- { "-resolution", ".resolution", XrmoptionSepArg, 0 },
- { "-thickness", ".thickness", XrmoptionSepArg, 0 },
- { "-wobble", ".wobble", XrmoptionNoArg, "True" },
- { "+wobble", ".wobble", XrmoptionNoArg, "False" },
- { "-debug", ".debug", XrmoptionNoArg, "True" },
-};
-
-static argtype vars[] = {
- {&speed, "speed", "Speed", DEF_SPEED, t_Float},
- {&complexity_arg, "complexity", "Complexity", DEF_COMPLEXITY, t_Float},
- {&resolution_arg, "resolution", "Resolution", DEF_RESOLUTION, t_Int},
- {&thickness_arg, "thickness", "Thickness", DEF_THICKNESS, t_Float},
- {&wobble_p, "wobble", "Wobble", DEF_WOBBLE, t_Bool},
- {&debug_p, "debug", "Debug", DEF_DEBUG, t_Bool},
-};
-
-ENTRYPOINT ModeSpecOpt jigsaw_opts = {countof(opts), opts, countof(vars), vars, NULL};
-
-
-/* Returns a spline describing one edge of a puzzle piece of the given length.
- */
-static spline *
-make_puzzle_curve (int pixels)
-{
- double x0 = 0.0000, y0 = 0.0000;
- double x1 = 0.3333, y1 = 0.1000;
- double x2 = 0.4333, y2 = 0.0333;
- double x3 = 0.4666, y3 = -0.0666;
- double x4 = 0.3333, y4 = -0.1666;
- double x5 = 0.3666, y5 = -0.2900;
- double x6 = 0.5000, y6 = -0.3333;
-
- spline *s = make_spline(20);
- s->n_controls = 0;
-
-# define PT(x,y) \
- s->control_x[s->n_controls] = pixels * (x); \
- s->control_y[s->n_controls] = pixels * (y); \
- s->n_controls++
- PT ( x0, y0);
- PT ( x1, y1);
- PT ( x2, y2);
- PT ( x3, y3);
- PT ( x4, y4);
- PT ( x5, y5);
- PT ( x6, y6);
- PT (1-x5, y5);
- PT (1-x4, y4);
- PT (1-x3, y3);
- PT (1-x2, y2);
- PT (1-x1, y1);
- PT (1-x0, y0);
-# undef PT
-
- compute_spline (s);
- return s;
-}
-
-
-#ifdef HAVE_TESS
-
-static void
-tess_error_cb (GLenum errorCode)
-{
- fprintf (stderr, "%s: tesselation error: %s\n",
- progname, gluErrorString(errorCode));
- exit (0);
-}
-
-
-static void
-tess_combine_cb (GLdouble coords[3], GLdouble *d[4], GLfloat w[4],
- GLdouble **dataOut)
-{
- GLdouble *new = (GLdouble *) malloc (3 * sizeof(*new));
- new[0] = coords[0];
- new[1] = coords[1];
- new[2] = coords[2];
- *dataOut = new;
-}
-
-
-static void
-tess_vertex_cb (void *vertex_data, void *closure)
-{
- puzzle_piece *p = (puzzle_piece *) closure;
- GLdouble *v = (GLdouble *) vertex_data;
- GLdouble x = v[0];
- GLdouble y = v[1];
- GLdouble z = v[2];
-
- if (p)
- {
- GLfloat pw = p->jc->puzzle_width;
- GLfloat ph = p->jc->puzzle_height;
-
- GLfloat xx = x / (GLfloat) resolution_arg; /* 0-1 from piece origin */
- GLfloat yy = y / (GLfloat) resolution_arg;
- GLdouble tx = (p->home.x + xx) / pw; /* 0-1 from puzzle origin */
- GLdouble ty = (ph - p->home.y - yy) / ph;
-
- tx = p->jc->tex_x + (tx * p->jc->tex_width);
- ty = p->jc->tex_y + (ty * p->jc->tex_height);
-
- glTexCoord2d (tx, ty);
- }
-
- glVertex3d (x, y, z);
-}
-
-#else /* HAVE_TESS */
-
-/* Writes triangles into the array of floats.
- Returns the number of floats written (triangles * 9).
- */
-static int
-make_piece_eighth (jigsaw_configuration *jc, const spline *s,
- int resolution, int type, GLfloat *out,
- Bool flip_x, Bool flip_y, Bool rotate_p)
-{
- GLfloat *oout = out;
- int cx = resolution/2;
- int cy = resolution/2;
- int np = (s->n_points / 2) + 1;
- int last_x = -999999, last_y = -999999;
- Bool inflected = False;
- int i;
-
- if (type == FLAT)
- {
- *out++ = cx;
- *out++ = 0;
- *out++ = 0;
-
- *out++ = cx;
- *out++ = cy;
- *out++ = 0;
-
- *out++ = 0;
- *out++ = 0;
- *out++ = 0;
-
- goto END;
- }
-
- for (i = (type == IN ? np-1 : 0);
- (type == IN ? i >= 0 : i < np);
- i += (type == IN ? -1 : 1))
- {
- int x = s->points[i].x;
- int y = s->points[i].y;
-
- if (type == IN)
- y = -y;
-
- if (last_x != -999999)
- {
- if (!inflected &&
- (type == IN
- ? x >= last_x
- : x < last_x))
- {
- inflected = True;
-
- *out++ = cx;
- *out++ = cy;
- *out++ = 0;
-
- *out++ = last_x;
- *out++ = last_y;
- *out++ = 0;
-
- if (type == IN)
- {
- cx = 0;
- cy = 0;
- }
- else
- {
- cy = y;
- }
-
- *out++ = cx;
- *out++ = cy;
- *out++ = 0;
- }
-
- *out++ = cx;
- *out++ = cy;
- *out++ = 0;
-
- *out++ = last_x;
- *out++ = last_y;
- *out++ = 0;
-
- *out++ = x;
- *out++ = y;
- *out++ = 0;
- }
-
- last_x = x;
- last_y = y;
- }
- END:
-
- {
- int count = out - oout;
- Bool cw_p;
-
- if (flip_x)
- for (i = 0; i < count; i += 3)
- oout[i] = resolution - oout[i];
-
- if (flip_y)
- for (i = 0; i < count; i += 3)
- oout[i+1] = resolution - oout[i+1];
-
- cw_p = (type == IN);
- if (flip_x) cw_p = !cw_p;
- if (flip_y) cw_p = !cw_p;
-
- if (cw_p)
- for (i = 0; i < count; i += 9)
- {
- GLfloat x1 = oout[i+0];
- GLfloat y1 = oout[i+1];
- GLfloat x2 = oout[i+3];
- GLfloat y2 = oout[i+4];
- GLfloat x3 = oout[i+6];
- GLfloat y3 = oout[i+7];
- oout[i+0] = x2;
- oout[i+1] = y2;
- oout[i+3] = x1;
- oout[i+4] = y1;
- oout[i+6] = x3;
- oout[i+7] = y3;
- }
-
- if (rotate_p)
- for (i = 0; i < count; i += 3)
- {
- GLfloat x = oout[i];
- GLfloat y = oout[i+1];
- oout[i] = resolution - y;
- oout[i+1] = x;
- }
-
- return count;
- }
-}
-
-#endif /* !HAVE_TESS */
-
-
-
-/* Draws a puzzle piece. The top/right/bottom/left_type args
- indicate the direction the tabs point: 1 for out, -1 for in, 0 for flat.
- */
-static int
-draw_piece (jigsaw_configuration *jc, puzzle_piece *p,
- int resolution, GLfloat thickness,
- int top_type, int right_type,
- int bottom_type, int left_type,
- Bool wire)
-{
- spline *s = make_puzzle_curve (resolution);
- GLdouble *pts = (GLdouble *) malloc (s->n_points * 4 * 3 * sizeof(*pts));
- int polys = 0;
- int i, o;
- GLdouble z = resolution * thickness;
-
- o = 0;
- if (top_type == 0) {
- pts[o++] = 0;
- pts[o++] = 0;
- pts[o++] = z;
-
- pts[o++] = resolution;
- pts[o++] = 0;
- pts[o++] = z;
- } else {
- for (i = 0; i < s->n_points; i++) {
- pts[o++] = s->points[i].x;
- pts[o++] = s->points[i].y * top_type;
- pts[o++] = z;
- }
- }
-
- if (right_type == 0) {
- pts[o++] = resolution;
- pts[o++] = resolution;
- pts[o++] = z;
- } else {
- for (i = 1; i < s->n_points; i++) {
- pts[o++] = resolution + s->points[i].y * (-right_type);
- pts[o++] = s->points[i].x;
- pts[o++] = z;
- }
- }
-
- if (bottom_type == 0) {
- pts[o++] = 0;
- pts[o++] = resolution;
- pts[o++] = z;
- } else {
- for (i = 1; i < s->n_points; i++) {
- pts[o++] = s->points[s->n_points-i-1].x;
- pts[o++] = resolution + s->points[s->n_points-i-1].y * (-bottom_type);
- pts[o++] = z;
- }
- }
-
- if (left_type == 0) {
- pts[o++] = 0;
- pts[o++] = 0;
- pts[o++] = z;
- } else {
- for (i = 1; i < s->n_points; i++) {
- pts[o++] = s->points[s->n_points-i-1].y * left_type;
- pts[o++] = s->points[s->n_points-i-1].x;
- pts[o++] = z;
- }
- }
-
- { GLfloat ss = 1.0 / resolution; glScalef (ss, ss, ss); }
-
-# ifndef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
- glPolygonMode (GL_FRONT_AND_BACK, wire ? GL_LINE : GL_FILL);
-# endif
-
- if (wire)
- {
- glDisable (GL_TEXTURE_2D);
- glDisable (GL_BLEND);
- glDisable (GL_LIGHTING);
- }
- else
- {
-# ifdef HAVE_TESS
-
-# ifndef _GLUfuncptr
-# define _GLUfuncptr void(*)(void)
-# endif
- GLUtesselator *tess = gluNewTess();
- gluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr)glBegin);
- gluTessCallback(tess, GLU_TESS_VERTEX_DATA,(_GLUfuncptr)tess_vertex_cb);
- gluTessCallback(tess, GLU_TESS_END, (_GLUfuncptr)glEnd);
- gluTessCallback(tess, GLU_TESS_COMBINE, (_GLUfuncptr)tess_combine_cb);
- gluTessCallback(tess, GLU_TESS_ERROR, (_GLUfuncptr)tess_error_cb);
-
- /* front face */
- glEnable (GL_TEXTURE_2D);
- glEnable (GL_BLEND);
- glEnable (GL_LIGHTING);
- glBindTexture(GL_TEXTURE_2D, jc->texid);
- glFrontFace (GL_CCW);
- glNormal3f (0, 0, 1);
- gluTessBeginPolygon (tess, p);
- gluTessBeginContour (tess);
- for (i = 0; i < o; i += 3)
- {
- GLdouble *p = pts + i;
- gluTessVertex (tess, p, p);
- polys++; /* not quite right but close */
- }
- gluTessEndContour(tess);
- gluTessEndPolygon(tess);
-
- /* back face */
- glDisable (GL_TEXTURE_2D);
- glFrontFace (GL_CW);
- glNormal3f (0, 0, -1);
- gluTessBeginPolygon (tess, 0);
- gluTessBeginContour (tess);
- for (i = 0; i < o; i += 3)
- {
- GLdouble *p = pts + i;
- p[2] = -p[2];
- gluTessVertex (tess, p, p);
- polys++; /* not quite right but close */
- }
- gluTessEndContour(tess);
- gluTessEndPolygon(tess);
- gluDeleteTess(tess);
-
- /* Put it back */
- for (i = 0; i < o; i += 3)
- {
- GLdouble *p = pts + i;
- p[2] = -p[2];
- }
-
-# else /* !HAVE_TESS */
-
- GLfloat *tri = (GLfloat *)
- malloc (s->n_points * 4 * 3 * 3 * sizeof(*tri));
- GLfloat *otri = tri;
- int count;
- GLdouble zz;
-
- tri += make_piece_eighth (jc, s, resolution, top_type, tri, 0, 0, 0);
- tri += make_piece_eighth (jc, s, resolution, top_type, tri, 1, 0, 0);
- tri += make_piece_eighth (jc, s, resolution, left_type, tri, 0, 1, 1);
- tri += make_piece_eighth (jc, s, resolution, left_type, tri, 1, 1, 1);
- tri += make_piece_eighth (jc, s, resolution, bottom_type, tri, 0, 1, 0);
- tri += make_piece_eighth (jc, s, resolution, bottom_type, tri, 1, 1, 0);
- tri += make_piece_eighth (jc, s, resolution, right_type, tri, 0, 0, 1);
- tri += make_piece_eighth (jc, s, resolution, right_type, tri, 1, 0, 1);
- count = (tri - otri) / 9;
-
- if (! wire)
- {
- glEnable (GL_TEXTURE_2D);
- glEnable (GL_BLEND);
- glEnable (GL_LIGHTING);
- glBindTexture(GL_TEXTURE_2D, jc->texid);
- }
-
- for (zz = z; zz >= -z; zz -= 2*z)
- {
- int i;
- glFrontFace (zz > 0 ? GL_CCW : GL_CW);
- glNormal3f (0, 0, (zz > 0 ? 1 : -1));
-
- if (zz < 0)
- glDisable (GL_TEXTURE_2D); /* back face */
-
- glPushMatrix();
- glTranslatef (0, 0, zz);
-
- tri = otri;
- if (wire)
- {
- for (i = 0; i < count; i++)
- {
- glBegin (GL_LINE_LOOP);
- glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
- glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
- glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
- glEnd();
- }
- }
- else
- {
- GLfloat pw = p->jc->puzzle_width;
- GLfloat ph = p->jc->puzzle_height;
- GLfloat r = resolution;
-
- glBegin (GL_TRIANGLES);
- for (i = 0; i < count * 3; i++)
- {
- GLfloat x = *tri++;
- GLfloat y = *tri++;
- GLfloat z = *tri++;
-
- /* 0-1 from piece origin */
- GLfloat xx = x / r;
- GLfloat yy = y / r;
-
- /* 0-1 from puzzle origin */
- GLfloat tx = (p->home.x + xx) / pw;
- GLfloat ty = (ph - p->home.y - yy) / ph;
-
- tx = p->jc->tex_x + (tx * p->jc->tex_width);
- ty = p->jc->tex_y + (ty * p->jc->tex_height);
-
- glTexCoord2f (tx, ty);
- glVertex3f (x, y, z);
- }
- glEnd();
- }
-
- polys += count;
- glPopMatrix();
- }
-
- free (otri);
-# endif /* !HAVE_TESS */
- }
-
- /* side faces */
-
- glFrontFace (GL_CCW);
- glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
- for (i = 0; i < o; i += 3)
- {
- int j = (i+o-3) % o;
- int k = (i+3) % o;
- GLdouble *p = pts + i;
- GLdouble *pj = pts + j;
- GLdouble *pk = pts + k;
-
- do_normal (pj[0], pj[1], pj[2],
- pj[0], pj[1], -pj[2],
- pk[0], pk[1], pk[2]);
-
- glVertex3f (p[0], p[1], p[2]);
- glVertex3f (p[0], p[1], -p[2]);
- polys++;
- }
- glEnd();
-
- if (! wire)
- glColor3f (0.3, 0.3, 0.3);
-
- /* outline the edges in gray */
-
- glDisable (GL_TEXTURE_2D);
- glDisable (GL_LIGHTING);
- glLineWidth (jc->line_thickness);
-
- glBegin (GL_LINE_LOOP);
- for (i = 0; i < o; i += 3)
- glVertex3f (pts[i], pts[i+1], pts[i+2]);
- glEnd();
- polys += o/3;
-
- glBegin (GL_LINE_LOOP);
- for (i = 0; i < o; i += 3)
- glVertex3f (pts[i], pts[i+1], -pts[i+2]);
- glEnd();
- polys += o/3;
-
- free_spline (s);
- free (pts);
-
- return polys;
-}
-
-
-static void
-free_puzzle_grid (jigsaw_configuration *jc)
-{
- int i;
- for (i = 0; i < jc->puzzle_width * jc->puzzle_height; i++)
- glDeleteLists (jc->puzzle[i].dlist, 1);
- free (jc->puzzle);
- jc->puzzle = 0;
- jc->puzzle_width = 0;
- jc->puzzle_height = 0;
-}
-
-
-static void
-make_puzzle_grid (ModeInfo *mi)
-{
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
- int wire = MI_IS_WIREFRAME(mi);
- int x, y;
- GLfloat size = (8 + (random() % 8)) * complexity_arg;
-
- if (jc->puzzle)
- free_puzzle_grid (jc);
-
- if (wire)
- jc->aspect = MI_WIDTH(mi) / (float) MI_HEIGHT(mi);
-
- if (jc->aspect >= 1.0)
- {
- jc->puzzle_width = size;
- jc->puzzle_height = (size + 0.5) / jc->aspect;
- }
- else
- {
- jc->puzzle_width = (size + 0.5) * jc->aspect;
- jc->puzzle_height = size;
- }
-
- if (jc->puzzle_width < 1) jc->puzzle_width = 1;
- if (jc->puzzle_height < 1) jc->puzzle_height = 1;
-
- if (debug_p)
- fprintf (stderr, "%s: grid %4d x %-4d (%.2f)\n", progname,
- jc->puzzle_width, jc->puzzle_height,
- ((float) jc->puzzle_width / jc->puzzle_height));
-
- jc->puzzle = (puzzle_piece *)
- calloc (jc->puzzle_width * (jc->puzzle_height+1), sizeof(*jc->puzzle));
-
- /* Randomize the right and bottom edge of each piece.
- Match the left edge of the piece to the right to our right edge.
- Match the top edge of the piece to the bottom to our bottom edge.
- */
- for (y = 0; y < jc->puzzle_height; y++)
- for (x = 0; x < jc->puzzle_width; x++)
- {
- puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
- puzzle_piece *r = &jc->puzzle [y * jc->puzzle_width + x+1];
- puzzle_piece *b = &jc->puzzle [(y+1) * jc->puzzle_width + x];
- p->edge[RIGHT] = (random() & 1) ? IN : OUT;
- p->edge[BOTTOM] = (random() & 1) ? IN : OUT;
- r->edge[LEFT] = p->edge[RIGHT] == IN ? OUT : IN;
- b->edge[TOP] = p->edge[BOTTOM] == IN ? OUT : IN;
- }
-
- /* tell each piece where it belongs. */
- for (y = 0; y < jc->puzzle_height; y++)
- for (x = 0; x < jc->puzzle_width; x++)
- {
- puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
- p->jc = jc;
- p->home.x = x;
- p->home.y = y;
- p->current = p->home;
-
- /* make sure the outer border is flat */
- if (p->home.x == 0) p->edge[LEFT] = FLAT;
- if (p->home.y == 0) p->edge[TOP] = FLAT;
- if (p->home.x == jc->puzzle_width-1) p->edge[RIGHT] = FLAT;
- if (p->home.y == jc->puzzle_height-1) p->edge[BOTTOM] = FLAT;
-
- /* generate the polygons */
- p->dlist = glGenLists (1);
- check_gl_error ("generating lists");
- if (p->dlist <= 0) abort();
-
- glNewList (p->dlist, GL_COMPILE);
- p->polys += draw_piece (jc, p,
- resolution_arg, thickness_arg,
- p->edge[TOP], p->edge[RIGHT],
- p->edge[BOTTOM], p->edge[LEFT],
- wire);
- glEndList();
- }
-}
-
-
-static void shuffle_grid (ModeInfo *mi);
-
-
-static void
-image_loaded_cb (const char *filename, XRectangle *geometry,
- int image_width, int image_height,
- int texture_width, int texture_height,
- void *closure)
-{
- ModeInfo *mi = (ModeInfo *) closure;
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
-
- jc->tex_x = geometry->x / (float) texture_width;
- jc->tex_y = geometry->y / (float) texture_height;
- jc->tex_width = geometry->width / (float) texture_width;
- jc->tex_height = geometry->height / (float) texture_height;
- jc->aspect = geometry->width / (float) geometry->height;
-
- if (debug_p)
- {
- fprintf (stderr, "%s: image %s\n", progname,
- (filename ? filename : "(null)"));
- fprintf (stderr, "%s: image %4d x %-4d + %4d + %-4d (%.2f)\n", progname,
- geometry->width, geometry->height, geometry->x, geometry->y,
- (float) geometry->width / geometry->height);
- fprintf (stderr, "%s: tex %4d x %-4d\n", progname,
- texture_width, texture_height);
- fprintf (stderr, "%s: tex %4.2f x %4.2f + %4.2f + %4.2f (%.2f)\n",
- progname,
- jc->tex_width, jc->tex_height, jc->tex_x, jc->tex_y,
- (jc->tex_width / jc->tex_height) *
- (texture_width / texture_height));
- }
-
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- make_puzzle_grid (mi);
-}
-
-
-static void
-load_image (ModeInfo *mi)
-{
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
- load_texture_async (mi->xgwa.screen, mi->window,
- *jc->glx_context, 0, 0,
- False, jc->texid,
- image_loaded_cb, mi);
-}
-
-
-/* Whether the two pieces are the same shape, when the second piece
- is rotated by the given degrees.
- */
-static Bool
-same_shape (puzzle_piece *p0, puzzle_piece *p1, int rotated_by)
-{
- switch (rotated_by)
- {
- case 0:
- return (p0->edge[0] == p1->edge[0] &&
- p0->edge[1] == p1->edge[1] &&
- p0->edge[2] == p1->edge[2] &&
- p0->edge[3] == p1->edge[3]);
- case 90:
- return (p0->edge[0] == p1->edge[1] &&
- p0->edge[1] == p1->edge[2] &&
- p0->edge[2] == p1->edge[3] &&
- p0->edge[3] == p1->edge[0]);
- case 180:
- return (p0->edge[0] == p1->edge[2] &&
- p0->edge[1] == p1->edge[3] &&
- p0->edge[2] == p1->edge[0] &&
- p0->edge[3] == p1->edge[1]);
- case 270:
- return (p0->edge[0] == p1->edge[3] &&
- p0->edge[1] == p1->edge[0] &&
- p0->edge[2] == p1->edge[1] &&
- p0->edge[3] == p1->edge[2]);
- default:
- abort();
- }
-}
-
-
-/* Returns the proper rotation for the piece at the given position.
- */
-static int
-proper_rotation (jigsaw_configuration *jc, puzzle_piece *p,
- double x, double y)
-{
- puzzle_piece *p1;
- int cx = x;
- int cy = y;
- if (cx != x) abort(); /* must be in integral position! */
- if (cy != y) abort();
- p1 = &jc->puzzle [cy * jc->puzzle_width + cx];
- if (same_shape (p, p1, 0)) return 0;
- if (same_shape (p, p1, 90)) return 90;
- if (same_shape (p, p1, 180)) return 180;
- if (same_shape (p, p1, 270)) return 270;
- abort(); /* these two pieces don't match in any rotation! */
-}
-
-
-/* Returns the piece currently at the given position.
- */
-static puzzle_piece *
-piece_at (jigsaw_configuration *jc, double x, double y)
-{
- int npieces = jc->puzzle_width * jc->puzzle_height;
- int i;
- int cx = x;
- int cy = y;
- if (cx != x) abort(); /* must be in integral position! */
- if (cy != y) abort();
-
- for (i = 0; i < npieces; i++)
- {
- puzzle_piece *p = &jc->puzzle [i];
- if (p->current.x == cx &&
- p->current.y == cy)
- return p;
- }
- abort(); /* no piece at that position? */
-}
-
-
-static void
-shuffle_grid (ModeInfo *mi)
-{
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
- int max_tries = jc->puzzle_width * jc->puzzle_height;
- int npieces = jc->puzzle_width * jc->puzzle_height;
- int i;
-
- for (i = 0; i < npieces; i++)
- {
- puzzle_piece *p0 = &jc->puzzle [i];
- puzzle_piece *p1 = 0;
- int k;
-
- for (k = 0; k < max_tries; k++)
- {
- p1 = &jc->puzzle [random() % npieces];
- if (same_shape (p0, p1, 0)) break;
- if (same_shape (p0, p1, 90)) break;
- if (same_shape (p0, p1, 180)) break;
- if (same_shape (p0, p1, 270)) break;
- p1 = 0; /* mismatch */
- }
- if (p1 && p0 != p1)
- {
- XYZR s;
- s = p0->current; p0->current = p1->current; p1->current = s;
- p0->current.r =
- proper_rotation (jc, p0, p0->current.x, p0->current.y);
- p1->current.r =
- proper_rotation (jc, p1, p1->current.x, p1->current.y);
- }
- }
-}
-
-
-/* We tend to accumulate floating point errors, e.g., z being 0.000001
- after a move. This makes sure float values that should be integral are.
- */
-static void
-smooth_grid (ModeInfo *mi)
-{
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
- int npieces = jc->puzzle_width * jc->puzzle_height;
- int i;
-
- for (i = 0; i < npieces; i++)
- {
- puzzle_piece *p = &jc->puzzle [i];
-# define SMOOTH(P) \
- P.x = (int) (P.x + 0.5); \
- P.y = (int) (P.y + 0.5); \
- P.z = (int) (P.z + 0.5); \
- P.r = (int) (P.r + 0.5)
- SMOOTH(p->home);
- SMOOTH(p->current);
- SMOOTH(p->from);
- SMOOTH(p->to);
- if (p->tick <= 0.0001) p->tick = 0.0;
- if (p->tick >= 0.9999) p->tick = 1.0;
- }
-}
-
-
-static void
-begin_scatter (ModeInfo *mi, Bool unscatter_p)
-{
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
- int npieces = jc->puzzle_width * jc->puzzle_height;
- int i;
- XYZR ctr = { 0, };
- ctr.x = jc->puzzle_width / 2;
- ctr.y = jc->puzzle_height / 2;
-
- for (i = 0; i < npieces; i++)
- {
- puzzle_piece *p = &jc->puzzle [i];
- XYZ a;
- double d, r, th;
- p->tick = -frand(1.0);
- p->from = p->current;
-
- a.x = p->from.x - ctr.x; /* position relative to center */
- a.y = p->from.y - ctr.y;
-
- r = sqrt (a.x*a.x + a.y*a.y);
- th = atan2 (a.x, a.y);
-
- d = MAX (jc->puzzle_width, jc->puzzle_height) * 2;
- r = r*r + d;
-
- p->to.x = ctr.x + (r * sin (th));
- p->to.y = ctr.y + (r * cos (th));
- p->to.z = p->from.z;
- p->to.r = ((int) p->from.r + (random() % 180)) % 360;
- p->arc_height = frand(10.0);
-
- if (unscatter_p)
- {
- XYZR s = p->to; p->to = p->from; p->from = s;
- p->current = p->from;
- }
- }
-}
-
-
-static Bool
-solved_p (ModeInfo *mi)
-{
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
- int npieces = jc->puzzle_width * jc->puzzle_height;
- int i;
-
- for (i = 0; i < npieces; i++)
- {
- puzzle_piece *p = &jc->puzzle [i];
- if (p->current.x != p->home.x ||
- p->current.y != p->home.y ||
- p->current.z != p->home.z)
- return False;
- }
- return True;
-}
-
-
-static void
-move_one_piece (ModeInfo *mi)
-{
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
- int npieces = jc->puzzle_width * jc->puzzle_height;
- int i;
-
- for (i = 0; i < npieces * 100; i++) /* shouldn't take that long */
- {
- int i = random() % npieces;
- puzzle_piece *p0 = &jc->puzzle [i];
- puzzle_piece *p1;
-
- if (p0->current.x == p0->home.x &&
- p0->current.y == p0->home.y &&
- p0->current.z == p0->home.z)
- continue; /* piece already solved - try again */
-
- /* swap with the piece occupying p0's home cell. */
- p1 = piece_at (jc, p0->home.x, p0->home.y);
-
- if (p0 == p1) abort(); /* should have caught this above */
-
- p0->tick = 0;
- p0->from = p0->current;
- p0->to = p1->current;
- p0->to.r = proper_rotation (jc, p0, p0->to.x, p0->to.y);
-
- p1->tick = 0;
- p1->from = p1->current;
- p1->to = p0->current;
- p1->to.r = proper_rotation (jc, p1, p1->to.x, p1->to.y);
-
- /* Try to avoid having them intersect each other in the air. */
- p0->arc_height = 0;
- p1->arc_height = 0;
- while (fabs (p0->arc_height - p1->arc_height) < 1.5)
- {
- p0->arc_height = 0.5 + frand(3.0);
- p1->arc_height = 1.0 + frand(3.0);
- }
-
-# define RTILT(V) \
- V = 90 - BELLRAND(180); \
- if (! (random() % 5)) V *= 2; \
- if (! (random() % 5)) V *= 2; \
- if (! (random() % 5)) V *= 2
- RTILT (p0->max_tilt);
- RTILT (p1->max_tilt);
-# undef RTILT
-
- if (debug_p)
- fprintf (stderr, "%s: swapping %2d,%-2d with %2d,%d\n", progname,
- (int) p0->from.x, (int) p0->from.y,
- (int) p1->from.x, (int) p1->from.y);
- return;
- }
-
- abort(); /* infinite loop! */
-}
-
-
-static Bool
-anim_tick (ModeInfo *mi)
-{
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
- int npieces = jc->puzzle_width * jc->puzzle_height;
- int i;
- Bool finished_p = True;
-
- if (jc->pausing > 0)
- {
- jc->pausing -= jc->tick_speed * speed;
- if (debug_p && jc->pausing <= 0)
- fprintf (stderr, "%s: (done pausing)\n", progname);
- return False;
- }
-
- for (i = 0; i < npieces; i++)
- {
- puzzle_piece *p = &jc->puzzle [i];
- double tt;
-
- if (p->tick >= 1.0) continue; /* this piece is done */
- finished_p = False; /* not done */
-
- p->tick += jc->tick_speed * speed;
- if (p->tick > 1.0) p->tick = 1.0;
-
- if (p->tick < 0.0) continue; /* not yet started */
-
- tt = 1 - sin (M_PI/2 - p->tick * M_PI/2);
-
- p->current.x = p->from.x + ((p->to.x - p->from.x) * tt);
- p->current.y = p->from.y + ((p->to.y - p->from.y) * tt);
- p->current.z = p->from.z + ((p->to.z - p->from.z) * tt);
- p->current.r = p->from.r + ((p->to.r - p->from.r) * tt);
-
- p->current.z += p->arc_height * sin (p->tick * M_PI);
-
- p->tilt = p->max_tilt * sin (p->tick * M_PI);
-
- }
-
- return finished_p;
-}
-
-
-static void
-loading_msg (ModeInfo *mi)
-{
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
- int wire = MI_IS_WIREFRAME(mi);
- const char *text = "Loading...";
- XCharStruct e;
- int w, h;
- texture_string_metrics (jc->texfont, text, &e, 0, 0);
- w = e.width;
- h = e.ascent + e.descent;
-
- if (wire) return;
-
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- if (! jc->loading_dlist)
- {
- GLfloat othick = jc->line_thickness;
- puzzle_piece P = { 0, };
- P.jc = jc;
- jc->loading_dlist = glGenLists (1);
- glNewList (jc->loading_dlist, GL_COMPILE);
- jc->line_thickness = 1;
- draw_piece (jc, &P,
- resolution_arg, thickness_arg,
- OUT, OUT, IN, OUT, True);
- jc->line_thickness = othick;
- glEndList();
- }
-
- glColor3f (0.2, 0.2, 0.4);
-
- glPushMatrix();
- {
- double x, y, z;
- get_position (jc->rot, &x, &y, &z, True);
- glRotatef (x * 360, 1, 0, 0);
- glRotatef (y * 360, 0, 1, 0);
- glRotatef (z * 360, 0, 0, 1);
- glScalef (5, 5, 5);
- glTranslatef (-0.5, -0.5, 0);
- glCallList (jc->loading_dlist);
- }
- glPopMatrix();
-
- glColor3f (0.7, 0.7, 1);
-
-
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
-
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
-
- {
- double rot = current_device_rotation();
- glRotatef(rot, 0, 0, 1);
- if ((rot > 45 && rot < 135) ||
- (rot < -45 && rot > -135))
- {
- GLfloat s = MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi);
- glScalef (s, 1/s, 1);
- }
- }
-
- glOrtho(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi), -1, 1);
- glTranslatef ((MI_WIDTH(mi) - w) / 2,
- (MI_HEIGHT(mi) - h) / 2,
- 0);
- glEnable (GL_TEXTURE_2D);
- glPolygonMode (GL_FRONT, GL_FILL);
- glDisable (GL_LIGHTING);
- glEnable (GL_BLEND);
- glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- print_texture_string (jc->texfont, text);
- glEnable (GL_DEPTH_TEST);
- glPopMatrix();
-
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
-
- glMatrixMode(GL_MODELVIEW);
-}
-
-
-static void
-animate (ModeInfo *mi)
-{
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
- double slow = 0.01;
- double fast = 0.04;
-
- if (jc->button_down_p && jc->state != PUZZLE_LOADING_MSG)
- return;
-
- switch (jc->state)
- {
- case PUZZLE_LOADING_MSG:
- if (! jc->puzzle)
- loading_msg (mi);
- /* fall through */
-
- case PUZZLE_LOADING:
- if (!jc->puzzle) break; /* still loading */
- jc->tick_speed = slow;
- shuffle_grid (mi);
- smooth_grid (mi);
- begin_scatter (mi, True);
- jc->pausing = 0;
- jc->state = PUZZLE_UNSCATTER;
- if (debug_p) fprintf (stderr, "%s: unscattering\n", progname);
- break;
-
- case PUZZLE_UNSCATTER:
- jc->tick_speed = slow;
- if (anim_tick (mi))
- {
- smooth_grid (mi);
- jc->pausing = 1.0;
- jc->state = PUZZLE_SOLVE;
- if (debug_p) fprintf (stderr, "%s: solving\n", progname);
- }
- break;
-
- case PUZZLE_SOLVE:
- jc->tick_speed = fast;
- if (anim_tick (mi))
- {
- smooth_grid (mi);
- if (solved_p (mi))
- {
- if (debug_p) fprintf (stderr, "%s: solved!\n", progname);
- begin_scatter (mi, False);
- jc->state = PUZZLE_SCATTER;
- jc->pausing = 3.0;
- if (debug_p) fprintf (stderr, "%s: scattering\n", progname);
- }
- else
- {
- move_one_piece (mi);
- jc->pausing = 0.3;
- }
- }
- break;
-
- case PUZZLE_SCATTER:
- jc->tick_speed = slow;
- if (anim_tick (mi))
- {
- free_puzzle_grid (jc);
- load_image (mi);
- jc->state = PUZZLE_LOADING;
- jc->pausing = 1.0;
- if (debug_p) fprintf (stderr, "%s: loading\n", progname);
- }
- break;
-
- default:
- abort();
- }
-}
-
-
-/* Window management, etc
- */
-ENTRYPOINT void
-reshape_jigsaw (ModeInfo *mi, int width, int height)
-{
- jigsaw_configuration *jc = &sps[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);
-
- {
- GLfloat s = (MI_WIDTH(mi) < MI_HEIGHT(mi)
- ? (MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi))
- : 1);
- glScalef (s, s, s);
- }
-
- glClear(GL_COLOR_BUFFER_BIT);
-
- jc->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
-}
-
-
-ENTRYPOINT Bool
-jigsaw_handle_event (ModeInfo *mi, XEvent *event)
-{
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
-
- if (gltrackball_event_handler (event, jc->trackball,
- MI_WIDTH (mi), MI_HEIGHT (mi),
- &jc->button_down_p))
- return True;
- else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
- {
- begin_scatter (mi, False);
- jc->state = PUZZLE_SCATTER;
- return True;
- }
-
- return False;
-}
-
-
-ENTRYPOINT void
-init_jigsaw (ModeInfo *mi)
-{
- jigsaw_configuration *jc;
- int wire = MI_IS_WIREFRAME(mi);
-
- MI_INIT (mi, sps);
- jc = &sps[MI_SCREEN(mi)];
- jc->glx_context = init_GL(mi);
-
- reshape_jigsaw (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
-
- if (!wire)
- {
- GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
- GLfloat amb[4] = {0.2, 0.2, 0.2, 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, pos);
- glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
- glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
- glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
- }
-
- jc->trackball = gltrackball_init (False);
- jc->rot = make_rotator (0, 0, 0, 0, speed * 0.002, True);
- jc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
-
- jc->state = PUZZLE_LOADING_MSG;
-
- resolution_arg /= complexity_arg;
-
-# ifndef HAVE_TESS
- /* If it's not even, we get crosses. */
- if (resolution_arg & 1)
- resolution_arg++;
-# endif /* !HAVE_TESS */
-
- if (wire)
- make_puzzle_grid (mi);
- else
- load_image (mi);
-}
-
-
-ENTRYPOINT void
-draw_jigsaw (ModeInfo *mi)
-{
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
- Display *dpy = MI_DISPLAY(mi);
- Window window = MI_WINDOW(mi);
- int wire = MI_IS_WIREFRAME(mi);
-
- if (!jc->glx_context)
- return;
-
- glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *jc->glx_context);
-
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- mi->polygon_count = 0;
-
- glPushMatrix ();
-/* glRotatef(current_device_rotation(), 0, 0, 1); */
- gltrackball_rotate (jc->trackball);
-
- animate (mi);
-
- if (wobble_p && jc->puzzle)
- {
- double x, y, z;
- double max = 60;
- get_position (jc->rot, &x, &y, &z, !jc->button_down_p);
- x = 1; /* always lean back */
- glRotatef (max/2 - x*max, 1, 0, 0);
- glRotatef (max/2 - z*max, 0, 1, 0);
- }
-
- if (jc->puzzle)
- {
- GLfloat s = 14.0 / jc->puzzle_height;
- int x, y;
-
- glEnable(GL_CULL_FACE);
- glEnable(GL_DEPTH_TEST);
- glEnable(GL_NORMALIZE);
- glEnable(GL_LINE_SMOOTH);
-
- glScalef (s, s, s);
- glTranslatef (-jc->puzzle_width / 2.0, -jc->puzzle_height / 2.0, 0);
-
- if (! wire)
- {
- glEnable (GL_TEXTURE_2D);
- glEnable (GL_BLEND);
- glEnable (GL_LIGHTING);
- glEnable (GL_LIGHT0);
- glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
-
- for (y = 0; y < jc->puzzle_height; y++)
- for (x = 0; x < jc->puzzle_width; x++)
- {
- puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
- glPushMatrix();
- glTranslatef (p->current.x, p->current.y, p->current.z);
- glTranslatef (0.5, 0.5, 0);
- glRotatef (p->current.r, 0, 0, 1);
- glRotatef (p->tilt, 0, 1, 0);
- glTranslatef (-0.5, -0.5, 0);
- glCallList(p->dlist);
- mi->polygon_count += p->polys;
- glPopMatrix();
- }
- }
-
- glPopMatrix ();
-
- if (mi->fps_p) do_fps (mi);
- glFinish();
-
- glXSwapBuffers(dpy, window);
-}
-
-
-ENTRYPOINT void
-free_jigsaw (ModeInfo *mi)
-{
- jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
-
- if (!jc->glx_context) return;
- glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *jc->glx_context);
-
- if (jc->trackball) free (jc->trackball);
- if (jc->rot) free_rotator (jc->rot);
- if (jc->texfont) free_texture_font (jc->texfont);
- free_puzzle_grid (jc);
- if (glIsList(jc->loading_dlist)) glDeleteLists(jc->loading_dlist, 1);
-}
-
-XSCREENSAVER_MODULE ("Jigsaw", jigsaw)
-
-#endif /* USE_GL */