diff options
Diffstat (limited to 'hacks/glx/jigsaw.c')
| -rw-r--r-- | hacks/glx/jigsaw.c | 1511 |
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 */ |
