From http://www.jwz.org/xscreensaver/xscreensaver-5.33.tar.gz
[xscreensaver] / hacks / glx / jigsaw.c
1 /* xscreensaver, Copyright (c) 1997-2015 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   XCharStruct e;
1161   int w, h;
1162   texture_string_metrics (jc->texfont, text, &e, 0, 0);
1163   w = e.width;
1164   h = e.ascent + e.descent;
1165
1166   if (wire) return;
1167
1168   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1169
1170   if (! jc->loading_dlist)
1171     {
1172       GLfloat othick = jc->line_thickness;
1173       puzzle_piece P = { 0, };
1174       P.jc = jc;
1175       jc->loading_dlist = glGenLists (1);
1176       glNewList (jc->loading_dlist, GL_COMPILE);
1177       jc->line_thickness = 1;
1178       draw_piece (jc, &P,
1179                   resolution_arg, thickness_arg,
1180                   OUT, OUT, IN, OUT, True);
1181       jc->line_thickness = othick;
1182       glEndList();
1183     }
1184
1185   glColor3f (0.2, 0.2, 0.4);
1186
1187   glPushMatrix();
1188   {
1189     double x, y, z;
1190     get_position (jc->rot, &x, &y, &z, True);
1191     glRotatef (x * 360, 1, 0, 0);
1192     glRotatef (y * 360, 0, 1, 0);
1193     glRotatef (z * 360, 0, 0, 1);
1194     glScalef (5, 5, 5);
1195     glTranslatef (-0.5, -0.5, 0);
1196     glCallList (jc->loading_dlist);
1197   }
1198   glPopMatrix();
1199
1200   glColor3f (0.7, 0.7, 1);
1201
1202
1203   glMatrixMode(GL_PROJECTION);
1204   glPushMatrix();
1205   glLoadIdentity();
1206
1207   glMatrixMode(GL_MODELVIEW);
1208   glPushMatrix();
1209   glLoadIdentity();
1210
1211   {
1212     double rot = current_device_rotation();
1213     glRotatef(rot, 0, 0, 1);
1214     if ((rot >  45 && rot <  135) ||
1215         (rot < -45 && rot > -135))
1216       {
1217         GLfloat s = MI_WIDTH(mi) / (GLfloat) MI_HEIGHT(mi);
1218         glScalef (s, 1/s, 1);
1219       }
1220   }
1221
1222   glOrtho(0, MI_WIDTH(mi), 0, MI_HEIGHT(mi), -1, 1);
1223   glTranslatef ((MI_WIDTH(mi)  - w) / 2,
1224                 (MI_HEIGHT(mi) - h) / 2,
1225                 0);
1226   glEnable (GL_TEXTURE_2D);
1227   glPolygonMode (GL_FRONT, GL_FILL);
1228   glDisable (GL_LIGHTING);
1229   glEnable (GL_BLEND);
1230   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1231   print_texture_string (jc->texfont, text);
1232   glEnable (GL_DEPTH_TEST);
1233   glPopMatrix();
1234
1235   glMatrixMode(GL_PROJECTION);
1236   glPopMatrix();
1237
1238   glMatrixMode(GL_MODELVIEW);
1239 }
1240
1241
1242 static void
1243 animate (ModeInfo *mi)
1244 {
1245   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1246   double slow = 0.01;
1247   double fast = 0.04;
1248
1249   if (jc->button_down_p && jc->state != PUZZLE_LOADING_MSG)
1250     return;
1251
1252   switch (jc->state)
1253     {
1254     case PUZZLE_LOADING_MSG:
1255       if (! jc->puzzle)
1256         loading_msg (mi);
1257       /* fall through */
1258
1259     case PUZZLE_LOADING:
1260       if (!jc->puzzle) break;   /* still loading */
1261       jc->tick_speed = slow;
1262       shuffle_grid (mi);
1263       smooth_grid (mi);
1264       begin_scatter (mi, True);
1265       jc->pausing = 0;
1266       jc->state = PUZZLE_UNSCATTER;
1267       if (debug_p) fprintf (stderr, "%s: unscattering\n", progname);
1268       break;
1269
1270     case PUZZLE_UNSCATTER:
1271       jc->tick_speed = slow;
1272       if (anim_tick (mi))
1273         {
1274           smooth_grid (mi);
1275           jc->pausing = 1.0;
1276           jc->state = PUZZLE_SOLVE;
1277           if (debug_p) fprintf (stderr, "%s: solving\n", progname);
1278         }
1279       break;
1280
1281     case PUZZLE_SOLVE:
1282       jc->tick_speed = fast;
1283       if (anim_tick (mi))
1284         {
1285           smooth_grid (mi);
1286           if (solved_p (mi))
1287             {
1288               if (debug_p) fprintf (stderr, "%s: solved!\n", progname);
1289               begin_scatter (mi, False);
1290               jc->state = PUZZLE_SCATTER;
1291               jc->pausing = 3.0;
1292               if (debug_p) fprintf (stderr, "%s: scattering\n", progname);
1293             }
1294           else
1295             {
1296               move_one_piece (mi);
1297               jc->pausing = 0.3;
1298             }
1299         }
1300       break;
1301
1302     case PUZZLE_SCATTER:
1303       jc->tick_speed = slow;
1304       if (anim_tick (mi))
1305         {
1306           free_puzzle_grid (jc);
1307           load_image (mi);
1308           jc->state = PUZZLE_LOADING;
1309           jc->pausing = 1.0;
1310           if (debug_p) fprintf (stderr, "%s: loading\n", progname);
1311         }
1312       break;
1313
1314     default:
1315       abort();
1316     }
1317 }
1318
1319
1320 /* Window management, etc
1321  */
1322 ENTRYPOINT void
1323 reshape_jigsaw (ModeInfo *mi, int width, int height)
1324 {
1325   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1326   GLfloat h = (GLfloat) height / (GLfloat) width;
1327
1328   glViewport (0, 0, (GLint) width, (GLint) height);
1329
1330   glMatrixMode(GL_PROJECTION);
1331   glLoadIdentity();
1332   gluPerspective (30.0, 1/h, 1.0, 100.0);
1333
1334   glMatrixMode(GL_MODELVIEW);
1335   glLoadIdentity();
1336   gluLookAt( 0.0, 0.0, 30.0,
1337              0.0, 0.0, 0.0,
1338              0.0, 1.0, 0.0);
1339
1340   glClear(GL_COLOR_BUFFER_BIT);
1341
1342   jc->line_thickness = (MI_IS_WIREFRAME (mi) ? 1 : MAX (1, height / 300.0));
1343 }
1344
1345
1346 ENTRYPOINT Bool
1347 jigsaw_handle_event (ModeInfo *mi, XEvent *event)
1348 {
1349   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1350
1351   if (gltrackball_event_handler (event, jc->trackball,
1352                                  MI_WIDTH (mi), MI_HEIGHT (mi),
1353                                  &jc->button_down_p))
1354     return True;
1355   else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
1356     {
1357       begin_scatter (mi, False);
1358       jc->state = PUZZLE_SCATTER;
1359       return True;
1360     }
1361
1362   return False;
1363 }
1364
1365
1366 ENTRYPOINT void 
1367 init_jigsaw (ModeInfo *mi)
1368 {
1369   jigsaw_configuration *jc;
1370   int wire = MI_IS_WIREFRAME(mi);
1371
1372   if (!sps) {
1373     sps = (jigsaw_configuration *)
1374       calloc (MI_NUM_SCREENS(mi), sizeof (jigsaw_configuration));
1375     if (!sps) {
1376       fprintf(stderr, "%s: out of memory\n", progname);
1377       exit(1);
1378     }
1379   }
1380   jc = &sps[MI_SCREEN(mi)];
1381   jc->glx_context = init_GL(mi);
1382
1383   reshape_jigsaw (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
1384
1385   if (!wire)
1386     {
1387       GLfloat pos[4] = {0.05, 0.07, 1.00, 0.0};
1388       GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0};
1389       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
1390       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
1391
1392       glLightfv(GL_LIGHT0, GL_POSITION, pos);
1393       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
1394       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
1395       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
1396     }
1397
1398   jc->trackball = gltrackball_init (False);
1399   jc->rot = make_rotator (0, 0, 0, 0, speed * 0.002, True);
1400   jc->texfont = load_texture_font (MI_DISPLAY(mi), "font");
1401
1402   jc->state = PUZZLE_LOADING_MSG;
1403
1404   resolution_arg /= complexity_arg;
1405
1406 # ifndef HAVE_TESS
1407   /* If it's not even, we get crosses. */
1408   if (resolution_arg & 1)
1409     resolution_arg++;
1410 # endif /* !HAVE_TESS */
1411
1412   if (wire)
1413     make_puzzle_grid (mi);
1414   else
1415     load_image (mi);
1416 }
1417
1418
1419 ENTRYPOINT void
1420 draw_jigsaw (ModeInfo *mi)
1421 {
1422   jigsaw_configuration *jc = &sps[MI_SCREEN(mi)];
1423   Display *dpy = MI_DISPLAY(mi);
1424   Window window = MI_WINDOW(mi);
1425   int wire = MI_IS_WIREFRAME(mi);
1426
1427   if (!jc->glx_context)
1428     return;
1429
1430   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(jc->glx_context));
1431
1432   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1433
1434   mi->polygon_count = 0;
1435
1436   glPushMatrix ();
1437   glRotatef(current_device_rotation(), 0, 0, 1);
1438   gltrackball_rotate (jc->trackball);
1439
1440   animate (mi);
1441
1442   if (wobble_p && jc->puzzle)
1443     {
1444       double x, y, z;
1445       double max = 60;
1446       get_position (jc->rot, &x, &y, &z, !jc->button_down_p);
1447       x = 1; /* always lean back */
1448       glRotatef (max/2 - x*max, 1, 0, 0);
1449       glRotatef (max/2 - z*max, 0, 1, 0);
1450     }
1451
1452   if (jc->puzzle)
1453     {
1454       GLfloat s = 14.0 / jc->puzzle_height;
1455       int x, y;
1456
1457       glEnable(GL_CULL_FACE);
1458       glEnable(GL_DEPTH_TEST);
1459       glEnable(GL_NORMALIZE);
1460       glEnable(GL_LINE_SMOOTH);
1461
1462       glScalef (s, s, s);
1463       glTranslatef (-jc->puzzle_width / 2.0, -jc->puzzle_height / 2.0, 0);
1464
1465       if (! wire)
1466         {
1467           glEnable (GL_TEXTURE_2D);
1468           glEnable (GL_BLEND);
1469           glEnable (GL_LIGHTING);
1470           glEnable (GL_LIGHT0);
1471           glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1472         }
1473
1474       for (y = 0; y < jc->puzzle_height; y++)
1475         for (x = 0; x < jc->puzzle_width; x++)
1476           {
1477             puzzle_piece *p = &jc->puzzle [y * jc->puzzle_width + x];
1478             glPushMatrix();
1479             glTranslatef (p->current.x, p->current.y, p->current.z);
1480             glTranslatef (0.5, 0.5, 0);
1481             glRotatef (p->current.r, 0, 0, 1);
1482             glRotatef (p->tilt, 0, 1, 0);
1483             glTranslatef (-0.5, -0.5, 0);
1484             glCallList(p->dlist);
1485             mi->polygon_count += p->polys;
1486             glPopMatrix();
1487           }
1488     }
1489
1490   glPopMatrix ();
1491
1492   if (mi->fps_p) do_fps (mi);
1493   glFinish();
1494
1495   glXSwapBuffers(dpy, window);
1496 }
1497
1498 XSCREENSAVER_MODULE ("Jigsaw", jigsaw)
1499
1500 #endif /* USE_GL */