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