From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / glx / jigsaw.c
1 /* xscreensaver, Copyright (c) 1997-2014 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  *
11  * Written as an Xlib program some time in 1997.
12  * Rewritten as an OpenGL program 24-Aug-2008.
13  */
14
15 /*
16   Currently, we do this:
17
18     - start pieces off screen and move them in.
19     - when they land, they fill the puzzle grid with a shuffled
20       puzzle (pieces are rotated, too).
21     - swap random pairs of pieces until the puzzle is solved.
22     - scatter the pieces off screen (resulting in black).
23     - load new image and repeat.
24
25   Another idea would be to show the puzzle being solved the way
26   a person would do it:
27
28     - start off with black screen.
29     - assume knowledge of size of grid (position of corners).
30     - find a corner piece, and place it.
31     - while puzzle unsolved:
32       - pick a random piece;
33       - if it is the correct piece for any open edge, place it;
34       - if it fits physically in any rotation at any open edge,
35         place it, then toss it back (show the fake-out).
36       - if it doesn't fit at all, don't animate it at all.
37
38    This would take a long time to solve, I think...
39
40    An even harder idea would involve building up completed "clumps"
41    and sliding them around (a coral growth / accretion approach)
42  */
43
44 #define DEF_SPEED        "1.0"
45 #define DEF_COMPLEXITY   "1.0"
46 #define DEF_RESOLUTION   "100"
47 #define DEF_THICKNESS    "0.06"
48 #define DEF_WOBBLE       "True"
49 #define DEF_DEBUG        "False"
50
51 #define DEF_FONT "-*-helvetica-bold-r-normal-*-240-*"
52 #define DEFAULTS  "*delay:              20000   \n" \
53                   "*showFPS:            False   \n" \
54                   "*font:            " DEF_FONT"\n" \
55                   "*wireframe:          False   \n" \
56                   "*desktopGrabber:   xscreensaver-getimage -no-desktop %s\n" \
57                   "*grabDesktopImages:  False   \n" \
58                   "*chooseRandomImages: True    \n"
59
60
61 # define refresh_jigsaw 0
62 # define release_jigsaw 0
63 #undef countof
64 #define countof(x) (sizeof((x))/sizeof((*x)))
65
66 #ifdef HAVE_COCOA
67 # include "jwxyz.h"
68 #else
69 # include <X11/Xlib.h>
70 # include <GL/gl.h>
71 # include <GL/glu.h>
72 #endif
73
74 #include "xlockmore.h"
75 #include "rotator.h"
76 #include "gltrackball.h"
77 #include "spline.h"
78 #include "normals.h"
79 #include "grab-ximage.h"
80 #include "texfont.h"
81
82 #ifdef HAVE_JWZGLES
83 # include "jwzgles.h"
84 #else /* !HAVE_JWZGLES */
85 # define HAVE_TESS
86 #endif /* !HAVE_JWZGLES */
87
88 #undef BELLRAND
89 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
90
91 #ifdef USE_GL /* whole file */
92
93 #define TOP    0
94 #define RIGHT  1
95 #define BOTTOM 2
96 #define LEFT   3
97
98 #define IN    -1
99 #define FLAT   0
100 #define OUT    1
101
102 typedef struct jigsaw_configuration jigsaw_configuration;
103
104 typedef struct {
105   double x,y,z,r;       /* position and Z rotation (in degrees) */
106 } XYZR;
107
108
109 typedef struct {
110   jigsaw_configuration *jc;
111   int edge[4];
112   GLuint dlist;
113   int polys;
114
115   XYZR home;            /* correct position in puzzle */
116   XYZR current;         /* where it is right now */
117   XYZR from, to;        /* in transit from A to B */
118   double tick;          /* 0-1.0, how far from A to B */
119   double arc_height;    /* height of apex of curved path from A to B */
120   double tilt, max_tilt;
121
122 } puzzle_piece;
123
124
125 struct jigsaw_configuration {
126   GLXContext *glx_context;
127   trackball_state *trackball;
128   rotator *rot;
129   Bool button_down_p;
130   texture_font_data *texfont;
131   GLuint loading_dlist;
132
133   int puzzle_width;
134   int puzzle_height;
135   puzzle_piece *puzzle;
136
137   enum { PUZZLE_LOADING_MSG,
138          PUZZLE_LOADING,
139          PUZZLE_UNSCATTER,
140          PUZZLE_SOLVE, 
141          PUZZLE_SCATTER } state;
142   double pausing;
143   double tick_speed;
144
145   GLuint texid;
146   GLfloat tex_x, tex_y, tex_width, tex_height, aspect;
147
148   GLuint line_thickness;
149 };
150
151 static jigsaw_configuration *sps = NULL;
152
153 static GLfloat speed;
154 static GLfloat complexity_arg;
155 static int resolution_arg;
156 static GLfloat thickness_arg;
157 static Bool wobble_p;
158 static Bool debug_p;
159
160 static XrmOptionDescRec opts[] = {
161   { "-speed",      ".speed",       XrmoptionSepArg, 0 },
162   { "-complexity", ".complexity",  XrmoptionSepArg, 0 },
163   { "-resolution", ".resolution",  XrmoptionSepArg, 0 },
164   { "-thickness",  ".thickness",   XrmoptionSepArg, 0 },
165   { "-wobble",     ".wobble",      XrmoptionNoArg, "True" },
166   { "+wobble",     ".wobble",      XrmoptionNoArg, "False" },
167   { "-debug",      ".debug",       XrmoptionNoArg, "True" },
168 };
169
170 static argtype vars[] = {
171   {&speed,          "speed",       "Speed",       DEF_SPEED,      t_Float},
172   {&complexity_arg, "complexity",  "Complexity",  DEF_COMPLEXITY, t_Float},
173   {&resolution_arg, "resolution",  "Resolution",  DEF_RESOLUTION, t_Int},
174   {&thickness_arg,  "thickness",   "Thickness",   DEF_THICKNESS,  t_Float},
175   {&wobble_p,       "wobble",      "Wobble",      DEF_WOBBLE,     t_Bool},
176   {&debug_p,        "debug",       "Debug",       DEF_DEBUG,      t_Bool},
177 };
178
179 ENTRYPOINT ModeSpecOpt jigsaw_opts = {countof(opts), opts, countof(vars), vars, NULL};
180
181
182 /* Returns a spline describing one edge of a puzzle piece of the given length.
183  */
184 static spline *
185 make_puzzle_curve (int pixels)
186 {
187   double x0 = 0.0000, y0 =  0.0000;
188   double x1 = 0.3333, y1 =  0.1000;
189   double x2 = 0.4333, y2 =  0.0333;
190   double x3 = 0.4666, y3 = -0.0666;
191   double x4 = 0.3333, y4 = -0.1666;
192   double x5 = 0.3666, y5 = -0.2900;
193   double x6 = 0.5000, y6 = -0.3333;
194
195   spline *s = make_spline(20);
196   s->n_controls = 0;
197
198 # define PT(x,y) \
199     s->control_x[s->n_controls] = pixels * (x); \
200     s->control_y[s->n_controls] = pixels * (y); \
201     s->n_controls++
202   PT (  x0, y0);
203   PT (  x1, y1);
204   PT (  x2, y2);
205   PT (  x3, y3);
206   PT (  x4, y4);
207   PT (  x5, y5);
208   PT (  x6, y6);
209   PT (1-x5, y5);
210   PT (1-x4, y4);
211   PT (1-x3, y3);
212   PT (1-x2, y2);
213   PT (1-x1, y1);
214   PT (1-x0, y0);
215 # undef PT
216
217   compute_spline (s);
218   return s;
219 }
220
221
222 #ifdef HAVE_TESS
223
224 static void
225 tess_error_cb (GLenum errorCode)
226 {
227   fprintf (stderr, "%s: tesselation error: %s\n",
228            progname, gluErrorString(errorCode));
229   exit (0);
230 }
231
232
233 static void
234 tess_combine_cb (GLdouble coords[3], GLdouble *d[4], GLfloat w[4], 
235                  GLdouble **dataOut)
236 {
237   GLdouble *new = (GLdouble *) malloc (3 * sizeof(*new));
238   new[0] = coords[0];
239   new[1] = coords[1];
240   new[2] = coords[2];
241   *dataOut = new;
242 }
243
244
245 static void
246 tess_vertex_cb (void *vertex_data, void *closure)
247 {
248   puzzle_piece *p = (puzzle_piece *) closure;
249   GLdouble *v = (GLdouble *) vertex_data;
250   GLdouble x = v[0];
251   GLdouble y = v[1];
252   GLdouble z = v[2];
253
254   if (p)
255     {
256       GLfloat pw = p->jc->puzzle_width;
257       GLfloat ph = p->jc->puzzle_height;
258
259       GLfloat xx = x / (GLfloat) resolution_arg;  /* 0-1 from piece origin */
260       GLfloat yy = y / (GLfloat) resolution_arg;
261       GLdouble tx = (p->home.x + xx)      / pw;   /* 0-1 from puzzle origin */
262       GLdouble ty = (ph - p->home.y - yy) / ph;
263
264       tx = p->jc->tex_x + (tx * p->jc->tex_width);
265       ty = p->jc->tex_y + (ty * p->jc->tex_height);
266
267       glTexCoord2d (tx, ty);
268     }
269
270   glVertex3d (x, y, z);
271 }
272
273 #else  /* HAVE_TESS */
274
275 /* Writes triangles into the array of floats.
276    Returns the number of floats written (triangles * 9).
277  */
278 static int
279 make_piece_eighth (jigsaw_configuration *jc, const spline *s,
280                    int resolution, int type, GLfloat *out,
281                    Bool flip_x, Bool flip_y, Bool rotate_p)
282 {
283   GLfloat *oout = out;
284   int cx = resolution/2;
285   int cy = resolution/2;
286   int np = (s->n_points / 2) + 1;
287   int last_x = -999999, last_y = -999999;
288   Bool inflected = False;
289   int i;
290
291   if (type == FLAT)
292     {
293       *out++ = cx;
294       *out++ = 0;
295       *out++ = 0;
296
297       *out++ = cx;
298       *out++ = cy;
299       *out++ = 0;
300
301       *out++ = 0;
302       *out++ = 0;
303       *out++ = 0;
304
305       goto END;
306     }
307
308   for (i = (type == IN ? np-1 : 0); 
309        (type == IN ? i >= 0 : i < np);
310        i += (type == IN ? -1 : 1))
311     {
312       int x = s->points[i].x;
313       int y = s->points[i].y;
314
315       if (type == IN)
316         y = -y;
317
318       if (last_x != -999999)
319         {
320           if (!inflected &&
321               (type == IN
322                ? x >= last_x 
323                : x < last_x))
324             {
325               inflected = True;
326
327               *out++ = cx;
328               *out++ = cy;
329               *out++ = 0;
330
331               *out++ = last_x;
332               *out++ = last_y;
333               *out++ = 0;
334
335               if (type == IN)
336                 {
337                   cx = 0;
338                   cy = 0;
339                 }
340               else
341                 {
342                   cy = y;
343                 }
344
345               *out++ = cx;
346               *out++ = cy;
347               *out++ = 0;
348             }
349
350           *out++ = cx;
351           *out++ = cy;
352           *out++ = 0;
353
354           *out++ = last_x;
355           *out++ = last_y;
356           *out++ = 0;
357
358           *out++ = x;
359           *out++ = y;
360           *out++ = 0;
361         }
362
363       last_x = x;
364       last_y = y;
365     }
366  END:
367
368   {
369     int count = out - oout;
370     Bool cw_p;
371
372     if (flip_x)
373       for (i = 0; i < count; i += 3)
374         oout[i] = resolution - oout[i];
375
376     if (flip_y)
377       for (i = 0; i < count; i += 3)
378         oout[i+1] = resolution - oout[i+1];
379
380     cw_p = (type == IN);
381     if (flip_x) cw_p = !cw_p;
382     if (flip_y) cw_p = !cw_p;
383
384     if (cw_p)
385       for (i = 0; i < count; i += 9)
386         {
387           GLfloat x1 = oout[i+0];
388           GLfloat y1 = oout[i+1];
389           GLfloat x2 = oout[i+3];
390           GLfloat y2 = oout[i+4];
391           GLfloat x3 = oout[i+6];
392           GLfloat y3 = oout[i+7];
393           oout[i+0] = x2;
394           oout[i+1] = y2;
395           oout[i+3] = x1;
396           oout[i+4] = y1;
397           oout[i+6] = x3;
398           oout[i+7] = y3;
399         }
400
401     if (rotate_p)
402       for (i = 0; i < count; i += 3)
403         {
404           GLfloat x = oout[i];
405           GLfloat y = oout[i+1];
406           oout[i]   = resolution - y;
407           oout[i+1] = x;
408         }
409
410     return count;
411   }
412 }
413
414 #endif /* !HAVE_TESS */
415
416
417
418 /* Draws a puzzle piece.  The top/right/bottom/left_type args
419    indicate the direction the tabs point: 1 for out, -1 for in, 0 for flat.
420  */
421 static int
422 draw_piece (jigsaw_configuration *jc, puzzle_piece *p,
423             int resolution, GLfloat thickness,
424             int top_type, int right_type,
425             int bottom_type, int left_type,
426             Bool wire)
427 {
428   spline *s = make_puzzle_curve (resolution);
429   GLdouble *pts = (GLdouble *) malloc (s->n_points * 4 * 3 * sizeof(*pts));
430   int polys = 0;
431   int i, o;
432   GLdouble z = resolution * thickness;
433
434   o = 0;
435   if (top_type == 0) {
436     pts[o++] = 0;
437     pts[o++] = 0; 
438     pts[o++] = z;
439
440     pts[o++] = resolution;
441     pts[o++] = 0;
442     pts[o++] = z;
443   } else {
444     for (i = 0; i < s->n_points; i++) {
445       pts[o++] = s->points[i].x;
446       pts[o++] = s->points[i].y * top_type;
447       pts[o++] = z;
448     }
449   }
450
451   if (right_type == 0) {
452     pts[o++] = resolution;
453     pts[o++] = resolution;
454     pts[o++] = z;
455   } else {
456     for (i = 1; i < s->n_points; i++) {
457       pts[o++] = resolution + s->points[i].y * (-right_type);
458       pts[o++] = s->points[i].x;
459       pts[o++] = z;
460     }
461   }
462
463   if (bottom_type == 0) {
464     pts[o++] = 0;
465     pts[o++] = resolution;
466     pts[o++] = z;
467   } else {
468     for (i = 1; i < s->n_points; i++) {
469       pts[o++] = s->points[s->n_points-i-1].x;
470       pts[o++] = resolution + s->points[s->n_points-i-1].y * (-bottom_type);
471       pts[o++] = z;
472     }
473   }
474
475   if (left_type == 0) {
476     pts[o++] = 0;
477     pts[o++] = 0;
478     pts[o++] = z;
479   } else {
480     for (i = 1; i < s->n_points; i++) {
481       pts[o++] = s->points[s->n_points-i-1].y * left_type;
482       pts[o++] = s->points[s->n_points-i-1].x;
483       pts[o++] = z;
484     }
485   }
486
487   { GLfloat ss = 1.0 / resolution; glScalef (ss, ss, ss); }
488
489 # ifndef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
490   glPolygonMode (GL_FRONT_AND_BACK, wire ? GL_LINE : GL_FILL);
491 # endif
492
493   if (wire)
494     {
495       glDisable (GL_TEXTURE_2D);
496       glDisable (GL_BLEND);
497       glDisable (GL_LIGHTING);
498     }
499   else
500     {
501 # ifdef HAVE_TESS
502
503 #  ifndef  _GLUfuncptr
504 #   define _GLUfuncptr void(*)(void)
505 #  endif
506       GLUtesselator *tess = gluNewTess();
507       gluTessCallback(tess, GLU_TESS_BEGIN,      (_GLUfuncptr)glBegin);
508       gluTessCallback(tess, GLU_TESS_VERTEX_DATA,(_GLUfuncptr)tess_vertex_cb);
509       gluTessCallback(tess, GLU_TESS_END,        (_GLUfuncptr)glEnd);
510       gluTessCallback(tess, GLU_TESS_COMBINE,    (_GLUfuncptr)tess_combine_cb);
511       gluTessCallback(tess, GLU_TESS_ERROR,      (_GLUfuncptr)tess_error_cb);
512
513       /* front face */
514       glEnable (GL_TEXTURE_2D);
515       glEnable (GL_BLEND);
516       glEnable (GL_LIGHTING);
517       glBindTexture(GL_TEXTURE_2D, jc->texid);
518       glFrontFace (GL_CCW);
519       glNormal3f (0, 0, 1);
520       gluTessBeginPolygon (tess, p);
521       gluTessBeginContour (tess);
522       for (i = 0; i < o; i += 3)
523         {
524           GLdouble *p = pts + i;
525           gluTessVertex (tess, p, p);
526           polys++;  /* not quite right but close */
527         }
528       gluTessEndContour(tess);
529       gluTessEndPolygon(tess);
530
531       /* back face */
532       glDisable (GL_TEXTURE_2D);
533       glFrontFace (GL_CW);
534       glNormal3f (0, 0, -1);
535       gluTessBeginPolygon (tess, 0);
536       gluTessBeginContour (tess);
537       for (i = 0; i < o; i += 3)
538         {
539           GLdouble *p = pts + i;
540           p[2] = -p[2];
541           gluTessVertex (tess, p, p);
542           polys++;  /* not quite right but close */
543         }
544       gluTessEndContour(tess);
545       gluTessEndPolygon(tess);
546       gluDeleteTess(tess);
547
548       /* Put it back */
549       for (i = 0; i < o; i += 3)
550         {
551           GLdouble *p = pts + i;
552           p[2] = -p[2];
553         }
554
555 # else  /* !HAVE_TESS */
556
557       GLfloat *tri = (GLfloat *)
558         (GLfloat *) malloc (s->n_points * 4 * 3 * 3 * sizeof(*pts));
559       GLfloat *otri = tri;
560       int count;
561       GLdouble zz;
562
563       tri += make_piece_eighth (jc, s, resolution, top_type,    tri, 0, 0, 0);
564       tri += make_piece_eighth (jc, s, resolution, top_type,    tri, 1, 0, 0);
565       tri += make_piece_eighth (jc, s, resolution, left_type,   tri, 0, 1, 1);
566       tri += make_piece_eighth (jc, s, resolution, left_type,   tri, 1, 1, 1);
567       tri += make_piece_eighth (jc, s, resolution, bottom_type, tri, 0, 1, 0);
568       tri += make_piece_eighth (jc, s, resolution, bottom_type, tri, 1, 1, 0);
569       tri += make_piece_eighth (jc, s, resolution, right_type,  tri, 0, 0, 1);
570       tri += make_piece_eighth (jc, s, resolution, right_type,  tri, 1, 0, 1);
571       count = (tri - otri) / 9;
572
573       if (! wire)
574         {
575           glEnable (GL_TEXTURE_2D);
576           glEnable (GL_BLEND);
577           glEnable (GL_LIGHTING);
578           glBindTexture(GL_TEXTURE_2D, jc->texid);
579         }
580
581       for (zz = z; zz >= -z; zz -= 2*z)
582         {
583           int i;
584           glFrontFace (zz > 0 ? GL_CCW : GL_CW);
585           glNormal3f (0, 0, (zz > 0 ? 1 : -1));
586
587           if (zz < 0)
588             glDisable (GL_TEXTURE_2D);  /* back face */
589
590           glPushMatrix();
591           glTranslatef (0, 0, zz);
592
593           tri = otri;
594           if (wire)
595             {
596               for (i = 0; i < count; i++)
597                 {
598                   glBegin (GL_LINE_LOOP);
599                   glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
600                   glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
601                   glVertex3f (tri[0], tri[1], tri[2]); tri += 3;
602                   glEnd();
603                 }
604             }
605           else
606             {
607               GLfloat pw = p->jc->puzzle_width;
608               GLfloat ph = p->jc->puzzle_height;
609               GLfloat r = resolution;
610
611               glBegin (GL_TRIANGLES);
612               for (i = 0; i < count * 3; i++)
613                 {
614                   GLfloat x = *tri++;
615                   GLfloat y = *tri++;
616                   GLfloat z = *tri++;
617
618                   /* 0-1 from piece origin */
619                   GLfloat xx = x / r;
620                   GLfloat yy = y / r;
621
622                   /* 0-1 from puzzle origin */
623                   GLfloat tx = (p->home.x + xx)      / pw;
624                   GLfloat ty = (ph - p->home.y - yy) / ph;
625
626                   tx = p->jc->tex_x + (tx * p->jc->tex_width);
627                   ty = p->jc->tex_y + (ty * p->jc->tex_height);
628
629                   glTexCoord2f (tx, ty);
630                   glVertex3f (x, y, z);
631                 }
632               glEnd();
633             }
634
635           polys += count;
636           glPopMatrix();
637         }
638
639       free (otri);
640 # endif /* !HAVE_TESS */
641     }
642
643   /* side faces */
644
645   glFrontFace (GL_CCW);
646   glBegin (wire ? GL_LINES : GL_QUAD_STRIP);
647   for (i = 0; i < o; i += 3)
648     {
649       int j = (i+o-3) % o;
650       int k = (i+3)   % o;
651       GLdouble *p  = pts + i;
652       GLdouble *pj = pts + j;
653       GLdouble *pk = pts + k;
654
655       do_normal  (pj[0], pj[1],  pj[2],
656                   pj[0], pj[1], -pj[2],
657                   pk[0], pk[1],  pk[2]);
658
659       glVertex3f (p[0], p[1],  p[2]);
660       glVertex3f (p[0], p[1], -p[2]);
661       polys++;
662     }
663   glEnd();
664
665   if (! wire)
666     glColor3f (0.3, 0.3, 0.3);
667
668   /* outline the edges in gray */
669
670   glDisable (GL_TEXTURE_2D);
671   glDisable (GL_LIGHTING);
672   glLineWidth (jc->line_thickness);
673
674   glBegin (GL_LINE_LOOP);
675   for (i = 0; i < o; i += 3)
676     glVertex3f (pts[i], pts[i+1], pts[i+2]);
677   glEnd();
678   polys += o/3;
679
680   glBegin (GL_LINE_LOOP);
681   for (i = 0; i < o; i += 3)
682     glVertex3f (pts[i], pts[i+1], -pts[i+2]);
683   glEnd();
684   polys += o/3;
685
686   free_spline (s);
687   free (pts);
688
689   return polys;
690 }
691
692
693 static void
694 free_puzzle_grid (jigsaw_configuration *jc)
695 {
696   int i;
697   for (i = 0; i < jc->puzzle_width * jc->puzzle_height; i++)
698     glDeleteLists (jc->puzzle[i].dlist,  1);
699   free (jc->puzzle);
700   jc->puzzle = 0;
701   jc->puzzle_width = 0;
702   jc->puzzle_height = 0;
703 }
704
705
706 static void
707 make_puzzle_grid (ModeInfo *mi)
708 {
709   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
710   int wire = MI_IS_WIREFRAME(mi);
711   int x, y;
712   GLfloat size = (8 + (random() % 8)) * complexity_arg;
713
714   if (jc->puzzle)
715     free_puzzle_grid (jc);
716
717   if (wire)
718     jc->aspect = MI_WIDTH(mi) / (float) MI_HEIGHT(mi);
719
720   if (jc->aspect >= 1.0)
721     {
722       jc->puzzle_width  = size;
723       jc->puzzle_height = (size + 0.5) / jc->aspect;
724     }
725   else
726     {
727       jc->puzzle_width  = (size + 0.5) * jc->aspect;
728       jc->puzzle_height = size;
729     }
730
731   if (jc->puzzle_width  < 1) jc->puzzle_width  = 1;
732   if (jc->puzzle_height < 1) jc->puzzle_height = 1;
733
734   if (debug_p)
735     fprintf (stderr, "%s: grid  %4d x %-4d               (%.2f)\n", progname,
736              jc->puzzle_width, jc->puzzle_height,
737              ((float) jc->puzzle_width / jc->puzzle_height));
738
739   jc->puzzle = (puzzle_piece *) 
740     calloc (jc->puzzle_width * (jc->puzzle_height+1), sizeof(*jc->puzzle));
741
742   /* Randomize the right and bottom edge of each piece.
743      Match the left edge of the piece to the right to our right edge.
744      Match the top edge of the piece to the bottom to our bottom edge.
745    */
746   for (y = 0; y < jc->puzzle_height; y++)
747     for (x = 0; x < jc->puzzle_width; x++)
748       {
749         puzzle_piece *p = &jc->puzzle [y     * jc->puzzle_width + x];
750         puzzle_piece *r = &jc->puzzle [y     * jc->puzzle_width + x+1];
751         puzzle_piece *b = &jc->puzzle [(y+1) * jc->puzzle_width + x];
752         p->edge[RIGHT]  = (random() & 1) ? IN : OUT;
753         p->edge[BOTTOM] = (random() & 1) ? IN : OUT;
754         r->edge[LEFT]   = p->edge[RIGHT]  == IN ? OUT : IN;
755         b->edge[TOP]    = p->edge[BOTTOM] == IN ? OUT : IN;
756       }
757
758   /* tell each piece where it belongs. */
759   for (y = 0; y < jc->puzzle_height; y++)
760     for (x = 0; x < jc->puzzle_width; x++)
761       {
762         puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
763         p->jc = jc;
764         p->home.x = x;
765         p->home.y = y;
766         p->current = p->home;
767
768         /* make sure the outer border is flat */
769         if (p->home.x == 0)  p->edge[LEFT] = FLAT;
770         if (p->home.y == 0)  p->edge[TOP]  = FLAT;
771         if (p->home.x == jc->puzzle_width-1)  p->edge[RIGHT]  = FLAT;
772         if (p->home.y == jc->puzzle_height-1) p->edge[BOTTOM] = FLAT;
773
774         /* generate the polygons */
775         p->dlist = glGenLists (1);
776         check_gl_error ("generating lists");
777         if (p->dlist <= 0) abort();
778
779         glNewList (p->dlist, GL_COMPILE);
780         p->polys += draw_piece (jc, p,
781                                 resolution_arg, thickness_arg,
782                                 p->edge[TOP], p->edge[RIGHT],
783                                 p->edge[BOTTOM], p->edge[LEFT], 
784                                 wire);
785         glEndList();
786       }
787 }
788
789
790 static void shuffle_grid (ModeInfo *mi);
791
792
793 static void
794 image_loaded_cb (const char *filename, XRectangle *geometry,
795                  int image_width, int image_height, 
796                  int texture_width, int texture_height,
797                  void *closure)
798 {
799   ModeInfo *mi = (ModeInfo *) closure;
800   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
801
802   jc->tex_x      = geometry->x      / (float) texture_width;
803   jc->tex_y      = geometry->y      / (float) texture_height;
804   jc->tex_width  = geometry->width  / (float) texture_width; 
805   jc->tex_height = geometry->height / (float) texture_height;
806   jc->aspect     = geometry->width  / (float) geometry->height;
807
808   if (debug_p)
809     {
810       fprintf (stderr, "%s: image %s\n", progname, 
811                (filename ? filename : "(null)"));
812       fprintf (stderr, "%s: image %4d x %-4d + %4d + %-4d (%.2f)\n", progname,
813                geometry->width, geometry->height, geometry->x, geometry->y,
814                (float) geometry->width / geometry->height);
815       fprintf (stderr, "%s: tex   %4d x %-4d\n", progname,
816                texture_width, texture_height);
817       fprintf (stderr, "%s: tex   %4.2f x %4.2f + %4.2f + %4.2f (%.2f)\n",
818                progname,
819                jc->tex_width, jc->tex_height, jc->tex_x, jc->tex_y,
820                (jc->tex_width / jc->tex_height) *
821                (texture_width / texture_height));
822     }
823
824   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
825   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
826   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
827   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
828
829   make_puzzle_grid (mi);
830 }
831
832
833 static void
834 load_image (ModeInfo *mi)
835 {
836   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
837   load_texture_async (mi->xgwa.screen, mi->window,
838                       *jc->glx_context, 0, 0, 
839                       False, jc->texid,
840                       image_loaded_cb, mi);
841 }
842
843
844 /* Whether the two pieces are the same shape, when the second piece
845    is rotated by the given degrees.
846  */
847 static Bool
848 same_shape (puzzle_piece *p0, puzzle_piece *p1, int rotated_by)
849 {
850   switch (rotated_by) 
851     {
852     case 0:
853       return (p0->edge[0] == p1->edge[0] &&
854               p0->edge[1] == p1->edge[1] &&
855               p0->edge[2] == p1->edge[2] &&
856               p0->edge[3] == p1->edge[3]);
857     case 90:
858       return (p0->edge[0] == p1->edge[1] &&
859               p0->edge[1] == p1->edge[2] &&
860               p0->edge[2] == p1->edge[3] &&
861               p0->edge[3] == p1->edge[0]);
862     case 180:
863       return (p0->edge[0] == p1->edge[2] &&
864               p0->edge[1] == p1->edge[3] &&
865               p0->edge[2] == p1->edge[0] &&
866               p0->edge[3] == p1->edge[1]);
867     case 270:
868       return (p0->edge[0] == p1->edge[3] &&
869               p0->edge[1] == p1->edge[0] &&
870               p0->edge[2] == p1->edge[1] &&
871               p0->edge[3] == p1->edge[2]);
872     default:
873       abort();
874     }
875 }
876
877
878 /* Returns the proper rotation for the piece at the given position. 
879  */
880 static int
881 proper_rotation (jigsaw_configuration *jc, puzzle_piece *p, 
882                  double x, double y)
883 {
884   puzzle_piece *p1;
885   int cx = x;
886   int cy = y;
887   if (cx != x) abort();  /* must be in integral position! */
888   if (cy != y) abort();
889   p1 = &jc->puzzle [cy * jc->puzzle_width + cx];
890   if (same_shape (p, p1, 0))   return 0;
891   if (same_shape (p, p1, 90))  return 90;
892   if (same_shape (p, p1, 180)) return 180;
893   if (same_shape (p, p1, 270)) return 270;
894   abort();              /* these two pieces don't match in any rotation! */
895 }
896
897
898 /* Returns the piece currently at the given position. 
899  */
900 static puzzle_piece *
901 piece_at (jigsaw_configuration *jc, double x, double y)
902 {
903   int npieces = jc->puzzle_width * jc->puzzle_height;
904   int i;
905   int cx = x;
906   int cy = y;
907   if (cx != x) abort();  /* must be in integral position! */
908   if (cy != y) abort();
909
910   for (i = 0; i < npieces; i++)
911     {
912       puzzle_piece *p = &jc->puzzle [i];
913       if (p->current.x == cx &&
914           p->current.y == cy)
915         return p;
916     }
917   abort();   /* no piece at that position? */
918 }
919
920
921 static void
922 shuffle_grid (ModeInfo *mi)
923 {
924   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
925   int max_tries = jc->puzzle_width * jc->puzzle_height;
926   int npieces = jc->puzzle_width * jc->puzzle_height;
927   int i;
928
929   for (i = 0; i < npieces; i++)
930     {
931       puzzle_piece *p0 = &jc->puzzle [i];
932       puzzle_piece *p1 = 0;
933       int k;
934
935       for (k = 0; k < max_tries; k++)
936         {
937           p1 = &jc->puzzle [random() % npieces];
938           if (same_shape (p0, p1, 0))   break;
939           if (same_shape (p0, p1, 90))  break;
940           if (same_shape (p0, p1, 180)) break;
941           if (same_shape (p0, p1, 270)) break;
942           p1 = 0;  /* mismatch */
943         }
944       if (p1 && p0 != p1)
945         {
946           XYZR s;
947           s = p0->current; p0->current = p1->current; p1->current = s;
948           p0->current.r = 
949             proper_rotation (jc, p0, p0->current.x, p0->current.y);
950           p1->current.r = 
951             proper_rotation (jc, p1, p1->current.x, p1->current.y);
952         }
953     }
954 }
955
956
957 /* We tend to accumulate floating point errors, e.g., z being 0.000001
958    after a move.  This makes sure float values that should be integral are.
959  */
960 static void
961 smooth_grid (ModeInfo *mi)
962 {
963   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
964   int npieces = jc->puzzle_width * jc->puzzle_height;
965   int i;
966
967   for (i = 0; i < npieces; i++)
968     {
969       puzzle_piece *p = &jc->puzzle [i];
970 # define SMOOTH(P) \
971         P.x = (int) (P.x + 0.5);                  \
972         P.y = (int) (P.y + 0.5);                  \
973         P.z = (int) (P.z + 0.5);                  \
974         P.r = (int) (P.r + 0.5)
975       SMOOTH(p->home);
976       SMOOTH(p->current);
977       SMOOTH(p->from);
978       SMOOTH(p->to);
979       if (p->tick <= 0.0001) p->tick = 0.0;
980       if (p->tick >= 0.9999) p->tick = 1.0;
981     }
982 }
983
984
985 static void
986 begin_scatter (ModeInfo *mi, Bool unscatter_p)
987 {
988   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
989   int npieces = jc->puzzle_width * jc->puzzle_height;
990   int i;
991   XYZR ctr = { 0, };
992   ctr.x = jc->puzzle_width  / 2;
993   ctr.y = jc->puzzle_height / 2;
994
995   for (i = 0; i < npieces; i++)
996     {
997       puzzle_piece *p = &jc->puzzle [i];
998       XYZ a;
999       double d, r, th;
1000       p->tick = -frand(1.0);
1001       p->from = p->current;
1002
1003       a.x = p->from.x - ctr.x;  /* position relative to center */
1004       a.y = p->from.y - ctr.y;
1005
1006       r = sqrt (a.x*a.x + a.y*a.y);
1007       th = atan2 (a.x, a.y);
1008
1009       d = MAX (jc->puzzle_width, jc->puzzle_height) * 2;
1010       r = r*r + d;
1011
1012       p->to.x = ctr.x + (r * sin (th));
1013       p->to.y = ctr.y + (r * cos (th));
1014       p->to.z = p->from.z;
1015       p->to.r = ((int) p->from.r + (random() % 180)) % 360;
1016       p->arc_height = frand(10.0);
1017
1018       if (unscatter_p)
1019         {
1020           XYZR s = p->to; p->to = p->from; p->from = s;
1021           p->current = p->from;
1022         }
1023     }
1024 }
1025
1026
1027 static Bool
1028 solved_p (ModeInfo *mi)
1029 {
1030   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1031   int npieces = jc->puzzle_width * jc->puzzle_height;
1032   int i;
1033
1034   for (i = 0; i < npieces; i++)
1035     {
1036       puzzle_piece *p = &jc->puzzle [i];
1037       if (p->current.x != p->home.x ||
1038           p->current.y != p->home.y ||
1039           p->current.z != p->home.z)
1040         return False;
1041     }
1042   return True;
1043 }
1044
1045
1046 static void
1047 move_one_piece (ModeInfo *mi)
1048 {
1049   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1050   int npieces = jc->puzzle_width * jc->puzzle_height;
1051   int i;
1052
1053   for (i = 0; i < npieces * 100; i++)  /* shouldn't take that long */
1054     {
1055       int i = random() % npieces;
1056       puzzle_piece *p0 = &jc->puzzle [i];
1057       puzzle_piece *p1;
1058
1059       if (p0->current.x == p0->home.x &&
1060           p0->current.y == p0->home.y &&
1061           p0->current.z == p0->home.z)
1062         continue;               /* piece already solved - try again */
1063
1064       /* swap with the piece occupying p0's home cell. */
1065       p1 = piece_at (jc, p0->home.x, p0->home.y);
1066
1067       if (p0 == p1) abort();    /* should have caught this above */
1068
1069       p0->tick = 0;
1070       p0->from = p0->current;
1071       p0->to   = p1->current;
1072       p0->to.r = proper_rotation (jc, p0, p0->to.x, p0->to.y);
1073
1074       p1->tick = 0;
1075       p1->from = p1->current;
1076       p1->to   = p0->current;
1077       p1->to.r = proper_rotation (jc, p1, p1->to.x, p1->to.y);
1078
1079       /* Try to avoid having them intersect each other in the air. */
1080       p0->arc_height = 0;
1081       p1->arc_height = 0;
1082       while (fabs (p0->arc_height - p1->arc_height) < 1.5)
1083         {
1084           p0->arc_height = 0.5 + frand(3.0);
1085           p1->arc_height = 1.0 + frand(3.0);
1086         }
1087
1088 # define RTILT(V) \
1089          V = 90 - BELLRAND(180);       \
1090          if (! (random() % 5)) V *= 2; \
1091          if (! (random() % 5)) V *= 2; \
1092          if (! (random() % 5)) V *= 2
1093       RTILT (p0->max_tilt);
1094       RTILT (p1->max_tilt);
1095 # undef RTILT
1096
1097       if (debug_p)
1098         fprintf (stderr, "%s: swapping %2d,%-2d with %2d,%d\n", progname,
1099                  (int) p0->from.x, (int) p0->from.y,
1100                  (int) p1->from.x, (int) p1->from.y);
1101       return;
1102     }
1103
1104   abort();  /* infinite loop! */
1105 }
1106
1107
1108 static Bool
1109 anim_tick (ModeInfo *mi)
1110 {
1111   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1112   int npieces = jc->puzzle_width * jc->puzzle_height;
1113   int i;
1114   Bool finished_p = True;
1115
1116   if (jc->pausing > 0)
1117     {
1118       jc->pausing -= jc->tick_speed * speed;
1119       if (debug_p && jc->pausing <= 0)
1120         fprintf (stderr, "%s:   (done pausing)\n", progname);
1121       return False;
1122     }
1123
1124   for (i = 0; i < npieces; i++)
1125     {
1126       puzzle_piece *p = &jc->puzzle [i];
1127       double tt;
1128
1129       if (p->tick >= 1.0) continue;     /* this piece is done */
1130       finished_p = False;               /* not done */
1131
1132       p->tick += jc->tick_speed * speed;
1133       if (p->tick > 1.0) p->tick = 1.0;
1134
1135       if (p->tick < 0.0) continue;      /* not yet started */
1136
1137       tt = 1 - sin (M_PI/2 - p->tick * M_PI/2);
1138
1139       p->current.x = p->from.x + ((p->to.x - p->from.x) * tt);
1140       p->current.y = p->from.y + ((p->to.y - p->from.y) * tt);
1141       p->current.z = p->from.z + ((p->to.z - p->from.z) * tt);
1142       p->current.r = p->from.r + ((p->to.r - p->from.r) * tt);
1143
1144       p->current.z += p->arc_height * sin (p->tick * M_PI);
1145
1146       p->tilt = p->max_tilt * sin (p->tick * M_PI);
1147
1148     }
1149
1150   return finished_p;
1151 }
1152
1153
1154 static void
1155 loading_msg (ModeInfo *mi)
1156 {
1157   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1158   int wire = MI_IS_WIREFRAME(mi);
1159   const char *text = "Loading...";
1160   int h;
1161   int w = texture_string_width (jc->texfont, text, &h);
1162
1163   if (wire) return;
1164
1165   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1166
1167   if (! jc->loading_dlist)
1168     {
1169       GLfloat othick = jc->line_thickness;
1170       puzzle_piece P = { 0, };
1171       P.jc = jc;
1172       jc->loading_dlist = glGenLists (1);
1173       glNewList (jc->loading_dlist, GL_COMPILE);
1174       jc->line_thickness = 1;
1175       draw_piece (jc, &P,
1176                   resolution_arg, thickness_arg,
1177                   OUT, OUT, IN, OUT, True);
1178       jc->line_thickness = othick;
1179       glEndList();
1180     }
1181
1182   glColor3f (0.2, 0.2, 0.4);
1183
1184   glPushMatrix();
1185   {
1186     double x, y, z;
1187     get_position (jc->rot, &x, &y, &z, True);
1188     glRotatef (x * 360, 1, 0, 0);
1189     glRotatef (y * 360, 0, 1, 0);
1190     glRotatef (z * 360, 0, 0, 1);
1191     glScalef (5, 5, 5);
1192     glTranslatef (-0.5, -0.5, 0);
1193     glCallList (jc->loading_dlist);
1194   }
1195   glPopMatrix();
1196
1197   glColor3f (0.7, 0.7, 1);
1198
1199
1200   glMatrixMode(GL_PROJECTION);
1201   glPushMatrix();
1202   glLoadIdentity();
1203
1204   glMatrixMode(GL_MODELVIEW);
1205   glPushMatrix();
1206   glLoadIdentity();
1207
1208   {
1209     double rot = current_device_rotation();
1210     glRotatef(rot, 0, 0, 1);
1211     if ((rot >  45 && rot <  135) ||
1212         (rot < -45 && rot > -135))
1213       {
1214         GLfloat s = MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi);
1215         glScalef (s, 1/s, 1);
1216       }
1217   }
1218
1219   glOrtho(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi), -1, 1);
1220   glTranslatef ((MI_WIDTH(mi)  - w) / 2,
1221                 (MI_HEIGHT(mi) - h) / 2,
1222                 0);
1223   glEnable (GL_TEXTURE_2D);
1224   glPolygonMode (GL_FRONT, GL_FILL);
1225   glDisable (GL_LIGHTING);
1226   glEnable (GL_BLEND);
1227   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1228   print_texture_string (jc->texfont, text);
1229   glEnable (GL_DEPTH_TEST);
1230   glPopMatrix();
1231
1232   glMatrixMode(GL_PROJECTION);
1233   glPopMatrix();
1234
1235   glMatrixMode(GL_MODELVIEW);
1236 }
1237
1238
1239 static void
1240 animate (ModeInfo *mi)
1241 {
1242   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1243   double slow = 0.01;
1244   double fast = 0.04;
1245
1246   if (jc->button_down_p && jc->state != PUZZLE_LOADING_MSG)
1247     return;
1248
1249   switch (jc->state)
1250     {
1251     case PUZZLE_LOADING_MSG:
1252       if (! jc->puzzle)
1253         loading_msg (mi);
1254       /* fall through */
1255
1256     case PUZZLE_LOADING:
1257       if (!jc->puzzle) break;   /* still loading */
1258       jc->tick_speed = slow;
1259       shuffle_grid (mi);
1260       smooth_grid (mi);
1261       begin_scatter (mi, True);
1262       jc->pausing = 0;
1263       jc->state = PUZZLE_UNSCATTER;
1264       if (debug_p) fprintf (stderr, "%s: unscattering\n", progname);
1265       break;
1266
1267     case PUZZLE_UNSCATTER:
1268       jc->tick_speed = slow;
1269       if (anim_tick (mi))
1270         {
1271           smooth_grid (mi);
1272           jc->pausing = 1.0;
1273           jc->state = PUZZLE_SOLVE;
1274           if (debug_p) fprintf (stderr, "%s: solving\n", progname);
1275         }
1276       break;
1277
1278     case PUZZLE_SOLVE:
1279       jc->tick_speed = fast;
1280       if (anim_tick (mi))
1281         {
1282           smooth_grid (mi);
1283           if (solved_p (mi))
1284             {
1285               if (debug_p) fprintf (stderr, "%s: solved!\n", progname);
1286               begin_scatter (mi, False);
1287               jc->state = PUZZLE_SCATTER;
1288               jc->pausing = 3.0;
1289               if (debug_p) fprintf (stderr, "%s: scattering\n", progname);
1290             }
1291           else
1292             {
1293               move_one_piece (mi);
1294               jc->pausing = 0.3;
1295             }
1296         }
1297       break;
1298
1299     case PUZZLE_SCATTER:
1300       jc->tick_speed = slow;
1301       if (anim_tick (mi))
1302         {
1303           free_puzzle_grid (jc);
1304           load_image (mi);
1305           jc->state = PUZZLE_LOADING;
1306           jc->pausing = 1.0;
1307           if (debug_p) fprintf (stderr, "%s: loading\n", progname);
1308         }
1309       break;
1310
1311     default:
1312       abort();
1313     }
1314 }
1315
1316
1317 /* Window management, etc
1318  */
1319 ENTRYPOINT void
1320 reshape_jigsaw (ModeInfo *mi, int width, int height)
1321 {
1322   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1323   GLfloat h = (GLfloat) height / (GLfloat) width;
1324
1325   glViewport (0, 0, (GLint) width, (GLint) height);
1326
1327   glMatrixMode(GL_PROJECTION);
1328   glLoadIdentity();
1329   gluPerspective (30.0, 1/h, 1.0, 100.0);
1330
1331   glMatrixMode(GL_MODELVIEW);
1332   glLoadIdentity();
1333   gluLookAt( 0.0, 0.0, 30.0,
1334              0.0, 0.0, 0.0,
1335              0.0, 1.0, 0.0);
1336
1337   glClear(GL_COLOR_BUFFER_BIT);
1338
1339   jc->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
1340 }
1341
1342
1343 ENTRYPOINT Bool
1344 jigsaw_handle_event (ModeInfo *mi, XEvent *event)
1345 {
1346   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1347
1348   if (gltrackball_event_handler (event, jc->trackball,
1349                                  MI_WIDTH (mi), MI_HEIGHT (mi),
1350                                  &jc->button_down_p))
1351     return True;
1352   else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
1353     {
1354       begin_scatter (mi, False);
1355       jc->state = PUZZLE_SCATTER;
1356       return True;
1357     }
1358
1359   return False;
1360 }
1361
1362
1363 ENTRYPOINT void 
1364 init_jigsaw (ModeInfo *mi)
1365 {
1366   jigsaw_configuration *jc;
1367   int wire = MI_IS_WIREFRAME(mi);
1368
1369   if (!sps) {
1370     sps = (jigsaw_configuration *)
1371       calloc (MI_NUM_SCREENS(mi), sizeof (jigsaw_configuration));
1372     if (!sps) {
1373       fprintf(stderr, "%s: out of memory\n", progname);
1374       exit(1);
1375     }
1376   }
1377   jc = &sps[MI_SCREEN(mi)];
1378   jc->glx_context = init_GL(mi);
1379
1380   reshape_jigsaw (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1381
1382   if (!wire)
1383     {
1384       GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
1385       GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
1386       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
1387       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
1388
1389       glLightfv(GL_LIGHT0, GL_POSITION, pos);
1390       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
1391       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
1392       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
1393     }
1394
1395   jc->trackball = gltrackball_init (False);
1396   jc->rot = make_rotator (0, 0, 0, 0, speed * 0.002, True);
1397   jc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
1398
1399   jc->state = PUZZLE_LOADING_MSG;
1400
1401   resolution_arg /= complexity_arg;
1402
1403 # ifndef HAVE_TESS
1404   /* If it's not even, we get crosses. */
1405   if (resolution_arg & 1)
1406     resolution_arg++;
1407 # endif /* !HAVE_TESS */
1408
1409   if (wire)
1410     make_puzzle_grid (mi);
1411   else
1412     load_image (mi);
1413 }
1414
1415
1416 ENTRYPOINT void
1417 draw_jigsaw (ModeInfo *mi)
1418 {
1419   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1420   Display *dpy = MI_DISPLAY(mi);
1421   Window window = MI_WINDOW(mi);
1422   int wire = MI_IS_WIREFRAME(mi);
1423
1424   if (!jc->glx_context)
1425     return;
1426
1427   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(jc->glx_context));
1428
1429   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1430
1431   mi->polygon_count = 0;
1432
1433   glPushMatrix ();
1434   glRotatef(current_device_rotation(), 0, 0, 1);
1435   gltrackball_rotate (jc->trackball);
1436
1437   animate (mi);
1438
1439   if (wobble_p && jc->puzzle)
1440     {
1441       double x, y, z;
1442       double max = 60;
1443       get_position (jc->rot, &x, &y, &z, !jc->button_down_p);
1444       x = 1; /* always lean back */
1445       glRotatef (max/2 - x*max, 1, 0, 0);
1446       glRotatef (max/2 - z*max, 0, 1, 0);
1447     }
1448
1449   if (jc->puzzle)
1450     {
1451       GLfloat s = 14.0 / jc->puzzle_height;
1452       int x, y;
1453
1454       glEnable(GL_CULL_FACE);
1455       glEnable(GL_DEPTH_TEST);
1456       glEnable(GL_NORMALIZE);
1457       glEnable(GL_LINE_SMOOTH);
1458
1459       glScalef (s, s, s);
1460       glTranslatef (-jc->puzzle_width / 2.0, -jc->puzzle_height / 2.0, 0);
1461
1462       if (! wire)
1463         {
1464           glEnable (GL_TEXTURE_2D);
1465           glEnable (GL_BLEND);
1466           glEnable (GL_LIGHTING);
1467           glEnable (GL_LIGHT0);
1468           glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1469         }
1470
1471       for (y = 0; y < jc->puzzle_height; y++)
1472         for (x = 0; x < jc->puzzle_width; x++)
1473           {
1474             puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
1475             glPushMatrix();
1476             glTranslatef (p->current.x, p->current.y, p->current.z);
1477             glTranslatef (0.5, 0.5, 0);
1478             glRotatef (p->current.r, 0, 0, 1);
1479             glRotatef (p->tilt, 0, 1, 0);
1480             glTranslatef (-0.5, -0.5, 0);
1481             glCallList(p->dlist);
1482             mi->polygon_count += p->polys;
1483             glPopMatrix();
1484           }
1485     }
1486
1487   glPopMatrix ();
1488
1489   if (mi->fps_p) do_fps (mi);
1490   glFinish();
1491
1492   glXSwapBuffers(dpy, window);
1493 }
1494
1495 XSCREENSAVER_MODULE ("Jigsaw", jigsaw)
1496
1497 #endif /* USE_GL */