e1c83eabbe157bf9215a912387dd72c3acb2e2b9
[xscreensaver] / hacks / glx / endgame.c
1 /*
2  * endgame.c
3  * plays through a chess game ending.  enjoy.
4  *
5  * version 1.0 - June 6, 2002
6  *
7  * Copyright (C) 2002-2008 Blair Tennessy (tennessb@unbc.ca)
8  *
9  * Permission to use, copy, modify, distribute, and sell this software and its
10  * documentation for any purpose is hereby granted without fee, provided that
11  * the above copyright notice appear in all copies and that both that
12  * copyright notice and this permission notice appear in supporting
13  * documentation.  No representations are made about the suitability of this
14  * software for any purpose.  It is provided "as is" without express or
15  * implied warranty.
16  */
17
18 #ifdef STANDALONE
19 #define DEFAULTS       "*delay:     20000 \n" \
20                        "*showFPS:   False \n" \
21                        "*wireframe: False \n" \
22
23 # define refresh_chess 0
24 # include "xlockmore.h"
25
26 #else
27 # include "xlock.h"
28 #endif
29
30 #ifdef USE_GL
31
32 #define BOARDSIZE 8
33 #define WHITES 5
34
35 #include "gltrackball.h"
36 #include "chessmodels.h"
37 #include "chessgames.h"
38
39 #undef countof
40 #define countof(x) (sizeof((x))/sizeof((*x)))
41
42 #define DEF_ROTATE      "True"
43 #define DEF_REFLECTIONS "True"
44 #define DEF_SHADOWS     "True"
45 #define DEF_SMOOTH      "True"
46 #define DEF_CLASSIC     "False"
47
48
49 static XrmOptionDescRec opts[] = {
50   {"+rotate", ".chess.rotate", XrmoptionNoArg, "false" },
51   {"-rotate", ".chess.rotate", XrmoptionNoArg, "true" },
52   {"+reflections", ".chess.reflections", XrmoptionNoArg, "false" },
53   {"-reflections", ".chess.reflections", XrmoptionNoArg, "true" },
54   {"+shadows", ".chess.shadows", XrmoptionNoArg, "false" },
55   {"-shadows", ".chess.shadows", XrmoptionNoArg, "true" },
56   {"+smooth", ".chess.smooth", XrmoptionNoArg, "false" },
57   {"-smooth", ".chess.smooth", XrmoptionNoArg, "true" },
58   {"+classic", ".chess.classic", XrmoptionNoArg, "false" },
59   {"-classic", ".chess.classic", XrmoptionNoArg, "true" },
60 };
61
62 static int rotate, reflections, smooth, shadows, classic;
63
64 static argtype vars[] = {
65   {&rotate,      "rotate",      "Rotate",      DEF_ROTATE, t_Bool},
66   {&reflections, "reflections", "Reflections", DEF_REFLECTIONS, t_Bool},
67   {&shadows,     "shadows",      "Shadows",    DEF_SHADOWS, t_Bool},
68   {&smooth,      "smooth",      "Smooth",      DEF_SMOOTH, t_Bool},
69   {&classic,     "classic",     "Classic",     DEF_CLASSIC, t_Bool},
70 };
71
72 ENTRYPOINT ModeSpecOpt chess_opts = {countof(opts), opts, countof(vars), vars, NULL};
73
74 #ifdef USE_MODULES
75 ModStruct   chess_description =
76 {"chess", "init_chess", "draw_chess", "release_chess",
77  "draw_chess", "init_chess", NULL, &chess_opts,
78  1000, 1, 2, 1, 4, 1.0, "",
79  "Chess", 0, NULL};
80
81 #endif
82
83 #define checkImageWidth 16
84 #define checkImageHeight 16
85
86 typedef struct {
87   GLXContext *glx_context;
88   Window window;
89   trackball_state *trackball;
90   Bool button_down_p;
91
92   ChessGame game;
93   int oldwhite;
94
95   /** definition of white/black (orange/gray) colors */
96   GLfloat colors[2][3];
97
98   GLubyte checkImage[checkImageWidth][checkImageHeight][3];
99   GLuint piecetexture, boardtexture;
100
101   int mpiece, tpiece, steps, done;
102   double from[2], to[2];
103   double dx, dz;
104   int moving, take, mc, count, wire;
105   double theta;
106
107   GLfloat position[4];
108   GLfloat position2[4];
109
110   GLfloat mod;
111
112   GLfloat ground[4];
113
114   int oldgame;
115
116   int poly_counts[PIECES];  /* polygon count of each type of piece */
117
118
119 } Chesscreen;
120
121 static Chesscreen *qs = NULL;
122
123 static const GLfloat MaterialShadow[] =   {0.0, 0.0, 0.0, 0.3};
124
125
126 /* i prefer silvertip */
127 static const GLfloat whites[WHITES][3] = 
128   {
129     {1.0, 0.55, 0.1},
130     {0.8, 0.52, 0.8},
131     {0.43, 0.54, 0.76},
132     {0.2, 0.2, 0.2},
133     {0.35, 0.60, 0.35},
134   };
135
136 static void build_colors(Chesscreen *cs) 
137 {
138
139   /* find new white */
140   int newwhite = cs->oldwhite;
141   while(newwhite == cs->oldwhite)
142     newwhite = random()%WHITES;
143   cs->oldwhite = newwhite;
144
145   cs->colors[0][0] = whites[cs->oldwhite][0];
146   cs->colors[0][1] = whites[cs->oldwhite][1];
147   cs->colors[0][2] = whites[cs->oldwhite][2];
148 }
149
150 /* build piece texture */
151 static void make_piece_texture(Chesscreen *cs) 
152 {
153   int i, j, c;
154
155   for (i = 0; i < checkImageWidth; i++) {
156     for (j = 0; j < checkImageHeight; j++) {
157       c = ((j%2) == 0 || i%2 == 0) ? 240 : 180+random()%16;
158       cs->checkImage[i][j][0] = (GLubyte) c;
159       cs->checkImage[i][j][1] = (GLubyte) c;
160       cs->checkImage[i][j][2] = (GLubyte) c;
161     }
162   }
163
164   glGenTextures(1, &cs->piecetexture);
165   glBindTexture(GL_TEXTURE_2D, cs->piecetexture);
166
167   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
168   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
169   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
170   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
171   glTexImage2D(GL_TEXTURE_2D, 0, 3, checkImageWidth, 
172                checkImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, 
173                &cs->checkImage[0][0]);
174 }
175
176 /* build board texture (uniform noise in [180,180+50]) */
177 static void make_board_texture(Chesscreen *cs) 
178 {
179   int i, j, c;
180
181   for (i = 0; i < checkImageWidth; i++) {
182     for (j = 0; j < checkImageHeight; j++) {
183       c = 180 + random()%51;
184       cs->checkImage[i][j][0] = (GLubyte) c;
185       cs->checkImage[i][j][1] = (GLubyte) c;
186       cs->checkImage[i][j][2] = (GLubyte) c;
187     }
188   }
189
190   glGenTextures(1, &cs->boardtexture);
191   glBindTexture(GL_TEXTURE_2D, cs->boardtexture);
192
193   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
194   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
195   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
196   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
197   glTexImage2D(GL_TEXTURE_2D, 0, 3, checkImageWidth, 
198                checkImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, 
199                &cs->checkImage[0][0]);
200 }
201
202 /** handle X event (trackball) */
203 ENTRYPOINT Bool chess_handle_event (ModeInfo *mi, XEvent *event) 
204 {
205   Chesscreen *cs = &qs[MI_SCREEN(mi)];
206
207   if(event->xany.type == ButtonPress && event->xbutton.button == Button1) {
208     cs->button_down_p = True;
209     gltrackball_start (cs->trackball,
210                        event->xbutton.x, event->xbutton.y,
211                        MI_WIDTH (mi), MI_HEIGHT (mi));
212     return True;
213   }
214   else if(event->xany.type == ButtonRelease 
215           && event->xbutton.button == Button1) {
216     cs->button_down_p = False;
217     return True;
218   }
219   else if (event->xany.type == ButtonPress &&
220            (event->xbutton.button == Button4 ||
221             event->xbutton.button == Button5 ||
222             event->xbutton.button == Button6 ||
223             event->xbutton.button == Button7))
224     {
225       gltrackball_mousewheel (cs->trackball, event->xbutton.button, 5,
226                               !event->xbutton.state);
227       return True;
228     }
229   else if(event->xany.type == MotionNotify && cs->button_down_p) {
230     gltrackball_track (cs->trackball,
231                        event->xmotion.x, event->xmotion.y,
232                        MI_WIDTH (mi), MI_HEIGHT (mi));
233     return True;
234   }
235   
236   return False;
237 }
238
239 static const GLfloat diffuse2[] = {1.0, 1.0, 1.0, 1.0};
240 /*static const GLfloat ambient2[] = {0.7, 0.7, 0.7, 1.0};*/
241 static const GLfloat shininess[] = {60.0};
242 static const GLfloat specular[] = {0.4, 0.4, 0.4, 1.0};
243
244 /* configure lighting */
245 static void setup_lights(Chesscreen *cs) 
246 {
247   glEnable(GL_LIGHTING);
248   glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
249   glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse2);
250   glEnable(GL_LIGHT0);
251
252 /*   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient2); */
253
254   glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
255   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
256
257   glLightfv(GL_LIGHT1, GL_SPECULAR, diffuse2);
258   glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse2);
259   glEnable(GL_LIGHT1);
260 }
261
262 /* draw pieces */
263 static void drawPieces(ModeInfo *mi, Chesscreen *cs) 
264 {
265   int i, j;
266
267   for(i = 0; i < BOARDSIZE; ++i) {
268     for(j = 0; j < BOARDSIZE; ++j) {
269       if(cs->game.board[i][j]) {        
270         int c = cs->game.board[i][j]/PIECES;
271         glColor3fv(cs->colors[c]);
272         glCallList(cs->game.board[i][j]%PIECES);
273         mi->polygon_count += cs->poly_counts[cs->game.board[i][j]%PIECES];
274       }
275       
276       glTranslatef(1.0, 0.0, 0.0);
277     }
278     
279     glTranslatef(-1.0*BOARDSIZE, 0.0, 1.0);
280   }
281
282   glTranslatef(0.0, 0.0, -1.0*BOARDSIZE);
283 }
284
285 /* draw pieces */
286 static void drawPiecesShadow(ModeInfo *mi, Chesscreen *cs) 
287 {
288   int i, j;
289
290   for(i = 0; i < BOARDSIZE; ++i) {
291     for(j = 0; j < BOARDSIZE; ++j) {
292       if(cs->game.board[i][j]) {        
293         glColor4f(0.0, 0.0, 0.0, 0.4);
294         glCallList(cs->game.board[i][j]%PIECES);
295         mi->polygon_count += cs->poly_counts[cs->game.board[i][j]%PIECES];
296       }
297       
298       glTranslatef(1.0, 0.0, 0.0);
299     }
300     
301     glTranslatef(-1.0*BOARDSIZE, 0.0, 1.0);
302   }
303
304   glTranslatef(0.0, 0.0, -1.0*BOARDSIZE);
305 }
306
307 /* draw a moving piece */
308 static void drawMovingPiece(ModeInfo *mi, Chesscreen *cs, int shadow) 
309 {
310   int piece = cs->mpiece % PIECES;
311
312   if (piece == NONE) return;
313
314   glPushMatrix();
315
316   if(shadow) glColor4fv(MaterialShadow);
317   else glColor3fv(cs->colors[cs->mpiece/PIECES]);
318
319   /** assume a queening.  should be more general */
320   if((cs->mpiece == PAWN  && fabs(cs->to[0]) < 0.01) || 
321      (cs->mpiece == BPAWN && fabs(cs->to[0]-7.0) < 0.01)) {
322     glTranslatef(cs->from[1]+cs->steps*cs->dx, 0.0, cs->from[0]+cs->steps*cs->dz);
323
324     glColor4f(shadow ? MaterialShadow[0] : cs->colors[cs->mpiece/7][0], 
325               shadow ? MaterialShadow[1] : cs->colors[cs->mpiece/7][1], 
326               shadow ? MaterialShadow[2] : cs->colors[cs->mpiece/7][2],
327               (fabs(50.0-cs->steps))/50.0);
328
329     piece = cs->steps < 50 ? PAWN : QUEEN;
330
331     /* what a kludge */
332     if(cs->steps == 99)
333       cs->mpiece = cs->mpiece == PAWN ? QUEEN : BQUEEN;
334   }
335   else if(cs->mpiece % PIECES == KNIGHT) {
336     /* If there is nothing in the path of a knight, move it by sliding,
337        just like the other pieces.  But if there are any pieces on the
338        middle two squares in its path, the knight would intersect them,
339        so in that case, move it in an airborne arc. */
340     GLfloat y;
341     int i, j;
342     Bool blocked_p = False;
343     int fromx = MIN(cs->from[1], cs->to[1]);
344     int fromy = MIN(cs->from[0], cs->to[0]);
345     int tox   = MAX(cs->from[1], cs->to[1]);
346     int toy   = MAX(cs->from[0], cs->to[0]);
347     if (fromx == tox-2) fromx = tox = fromx+1;
348     if (fromy == toy-2) fromy = toy = fromy+1;
349     for (i = fromy; i <= toy; i++) {
350       for (j = fromx; j <= tox; j++) {
351         if (cs->game.board[i][j]) {
352           blocked_p = True;
353           break;
354         }
355       }
356     }
357
358     if (!blocked_p)
359       goto SLIDE;
360
361     /* Move by hopping. */
362     y = 1.5 * sin (M_PI * cs->steps / 100.0);
363     glTranslatef(cs->from[1]+cs->steps*cs->dx, y,
364                  cs->from[0]+cs->steps*cs->dz);
365
366   } else {
367   SLIDE:
368     /* Move by sliding. */
369     glTranslatef(cs->from[1]+cs->steps*cs->dx, 0.0, cs->from[0]+cs->steps*cs->dz);
370   }
371
372
373   if(!cs->wire)
374     glEnable(GL_BLEND);
375   
376   glCallList(piece);
377   mi->polygon_count += cs->poly_counts[cs->mpiece % PIECES];
378
379   glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
380   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
381
382   glPopMatrix();
383
384   if(!cs->wire)
385     glDisable(GL_BLEND);
386 }
387
388 /** code to squish a taken piece */
389 static void drawTakePiece(ModeInfo *mi, Chesscreen *cs, int shadow) 
390 {
391   if(!cs->wire)
392     glEnable(GL_BLEND);
393
394   glColor4f(shadow ? MaterialShadow[0] : cs->colors[cs->tpiece/7][0], 
395             shadow ? MaterialShadow[1] : cs->colors[cs->tpiece/7][1], 
396             shadow ? MaterialShadow[2] : cs->colors[cs->tpiece/7][2],
397             (100-1.6*cs->steps)/100.0);
398
399   glTranslatef(cs->to[1], 0.0, cs->to[0]);
400   
401   if(cs->mpiece % PIECES == KNIGHT)
402     glScalef(1.0+cs->steps/100.0, 1.0, 1.0+cs->steps/100.0);
403   else
404     glScalef(1.0, 1 - cs->steps/50.0 > 0.01 ? 1 - cs->steps/50.0 : 0.01, 1.0);
405   glCallList(cs->tpiece % 7);
406   mi->polygon_count += cs->poly_counts[cs->tpiece % PIECES];
407   
408   if(!cs->wire)
409     glDisable(GL_BLEND);
410 }
411
412 /** draw board */
413 static void drawBoard(ModeInfo *mi, Chesscreen *cs) 
414 {
415   int i, j;
416
417   glBegin(GL_QUADS);
418
419   for(i = 0; i < BOARDSIZE; ++i)
420     for(j = 0; j < BOARDSIZE; ++j) {
421       double ma1 = (i+j)%2 == 0 ? cs->mod*i : 0.0;
422       double mb1 = (i+j)%2 == 0 ? cs->mod*j : 0.0;
423       double ma2 = (i+j)%2 == 0 ? cs->mod*(i+1.0) : 0.01;
424       double mb2 = (i+j)%2 == 0 ? cs->mod*(j+1.0) : 0.01;
425
426       /*glColor3fv(colors[(i+j)%2]);*/
427       glColor4f(cs->colors[(i+j)%2][0], cs->colors[(i+j)%2][1],
428                 cs->colors[(i+j)%2][2], 0.65);
429       
430       glNormal3f(0.0, 1.0, 0.0);
431 /*       glTexCoord2f(mod*i, mod*(j+1.0)); */
432       glTexCoord2f(ma1, mb2);
433       glVertex3f(i, 0.0, j + 1.0);
434 /*       glTexCoord2f(mod*(i+1.0), mod*(j+1.0)); */
435       glTexCoord2f(ma2, mb2);
436       glVertex3f(i + 1.0, 0.0, j + 1.0);
437       glTexCoord2f(ma2, mb1);
438 /*       glTexCoord2f(mod*(i+1.0), mod*j); */
439       glVertex3f(i + 1.0, 0.0, j);
440       glTexCoord2f(ma1, mb1);
441 /*       glTexCoord2f(mod*i, mod*j); */
442       glVertex3f(i, 0.0, j);
443
444       mi->polygon_count++;
445     }
446   glEnd();
447
448   {
449     GLfloat off = 0.01;
450     GLfloat w = BOARDSIZE;
451     GLfloat h = 0.1;
452
453     /* Give the board a slight lip. */
454     /* #### oops, normals are wrong here, but you can't tell */
455
456     glColor3f(0.3, 0.3, 0.3);
457     glBegin (GL_QUADS);
458     glVertex3f (0,  0, 0);
459     glVertex3f (0, -h, 0);
460     glVertex3f (0, -h, w);
461     glVertex3f (0,  0, w);
462
463     glVertex3f (0,  0, w);
464     glVertex3f (0, -h, w);
465     glVertex3f (w, -h, w);
466     glVertex3f (w,  0, w);
467
468     glVertex3f (w,  0, w);
469     glVertex3f (w, -h, w);
470     glVertex3f (w, -h, 0);
471     glVertex3f (w,  0, 0);
472
473     glVertex3f (w,  0, 0);
474     glVertex3f (w, -h, 0);
475     glVertex3f (0, -h, 0);
476     glVertex3f (0,  0, 0);
477
478     glVertex3f (0, -h, 0);
479     glVertex3f (w, -h, 0);
480     glVertex3f (w, -h, w);
481     glVertex3f (0, -h, w);
482     glEnd();
483     mi->polygon_count += 4;
484
485     /* Fill in the underside of the board with an invisible black box
486        to hide the reflections that are not on tiles.  Probably there's
487        a way to do this with stencils instead.
488      */
489     w -= off*2;
490     h = 5;
491
492     glPushMatrix();
493     glTranslatef (off, 0, off);
494     glDisable(GL_LIGHTING);
495     glColor3f(0,0,0);
496     glBegin (GL_QUADS);
497     glVertex3f (0,  0, 0);
498     glVertex3f (0, -h, 0);
499     glVertex3f (0, -h, w);
500     glVertex3f (0,  0, w);
501
502     glVertex3f (0,  0, w);
503     glVertex3f (0, -h, w);
504     glVertex3f (w, -h, w);
505     glVertex3f (w,  0, w);
506
507     glVertex3f (w,  0, w);
508     glVertex3f (w, -h, w);
509     glVertex3f (w, -h, 0);
510     glVertex3f (w,  0, 0);
511
512     glVertex3f (w,  0, 0);
513     glVertex3f (w, -h, 0);
514     glVertex3f (0, -h, 0);
515     glVertex3f (0,  0, 0);
516
517     glVertex3f (0, -h, 0);
518     glVertex3f (w, -h, 0);
519     glVertex3f (w, -h, w);
520     glVertex3f (0, -h, w);
521     glEnd();
522     mi->polygon_count += 4;
523     glPopMatrix();
524     if (!cs->wire)
525       glEnable(GL_LIGHTING);
526   }
527 }
528
529 static void draw_pieces(ModeInfo *mi, Chesscreen *cs, int wire) 
530 {
531   if (!cs->wire) {
532     glEnable(GL_TEXTURE_2D);
533     glBindTexture(GL_TEXTURE_2D, cs->piecetexture);
534     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
535
536     glColor4f(0.5, 0.5, 0.5, 1.0);
537     glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialShadow);
538   }
539
540   drawPieces(mi, cs);
541   if(cs->moving) drawMovingPiece(mi, cs, 0);
542   if(cs->take) drawTakePiece(mi, cs, 0);
543   glDisable(GL_TEXTURE_2D);
544 }
545
546 static void draw_shadow_pieces(ModeInfo *mi, Chesscreen *cs, int wire) 
547 {
548   if (!cs->wire) {
549     glEnable(GL_TEXTURE_2D);
550     glBindTexture(GL_TEXTURE_2D, cs->piecetexture);
551     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
552   }
553
554   /* use the stencil */
555   glDisable(GL_LIGHTING);
556   glDisable(GL_COLOR_MATERIAL);
557   glDisable(GL_DEPTH_TEST);
558   glDisable(GL_TEXTURE_2D);
559   glDisable(GL_BLEND);
560
561   glClear(GL_STENCIL_BUFFER_BIT);
562   glColorMask(0,0,0,0);
563   glEnable(GL_STENCIL_TEST);
564
565   glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFFL);
566   glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
567   
568
569   glPushMatrix();
570   glTranslatef(0.0, 0.001, 0.0);
571
572   /* draw the pieces */
573   drawPiecesShadow(mi, cs);
574   if(cs->moving) drawMovingPiece(mi, cs, shadows);
575   if(cs->take) drawTakePiece(mi, cs, shadows);
576
577   glPopMatrix();
578
579
580   /* turn on drawing into colour buffer */
581   glColorMask(1,1,1,1);
582
583   /* programming with effect */
584   glDisable(GL_LIGHTING);
585   glDisable(GL_COLOR_MATERIAL);
586   glDisable(GL_TEXTURE_2D);
587
588   /* now draw the union of the shadows */
589
590   /* 
591      <todo>
592      want to keep alpha values (alpha is involved in transition
593      effects of the active pieces).
594      </todo>
595   */
596   glStencilFunc(GL_NOTEQUAL, 0, 0xFFFFFFFFL);
597   glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
598
599   glEnable(GL_BLEND);
600
601   glColor4fv(MaterialShadow);
602
603   /* draw the board generously to fill the shadows */
604   glBegin(GL_QUADS);
605
606   glVertex3f(-1.0, 0.0, -1.0);
607   glVertex3f(-1.0, 0.0, BOARDSIZE + 1.0);
608   glVertex3f(1.0 + BOARDSIZE, 0.0, BOARDSIZE + 1.0);
609   glVertex3f(1.0 + BOARDSIZE, 0.0, -1.0);
610
611   glEnd();
612
613   glDisable(GL_STENCIL_TEST);
614
615   /* "pop" attributes */
616   glEnable(GL_TEXTURE_2D);
617   glEnable(GL_COLOR_MATERIAL);
618   glEnable(GL_LIGHTING);
619   glEnable(GL_CULL_FACE);
620
621
622
623 }
624
625 enum {X, Y, Z, W};
626 enum {A, B, C, D};
627
628 /* create a matrix that will project the desired shadow */
629 static void shadowmatrix(GLfloat shadowMat[4][4],
630                   GLfloat groundplane[4],
631                   GLfloat lightpos[4]) 
632 {
633   GLfloat dot;
634
635   /* find dot product between light position vector and ground plane normal */
636   dot = groundplane[X] * lightpos[X] +
637         groundplane[Y] * lightpos[Y] +
638         groundplane[Z] * lightpos[Z] +
639         groundplane[W] * lightpos[W];
640
641   shadowMat[0][0] = dot - lightpos[X] * groundplane[X];
642   shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y];
643   shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z];
644   shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W];
645
646   shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X];
647   shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y];
648   shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z];
649   shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W];
650
651   shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X];
652   shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y];
653   shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z];
654   shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W];
655
656   shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X];
657   shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y];
658   shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z];
659   shadowMat[3][3] = dot - lightpos[W] * groundplane[W];
660 }
661
662 /** reflectionboard */
663 static void draw_reflections(ModeInfo *mi, Chesscreen *cs) 
664 {
665   int i, j;
666
667   glEnable(GL_STENCIL_TEST);
668   glStencilFunc(GL_ALWAYS, 1, 1);
669   glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
670   glColorMask(0,0,0,0);
671   glDisable(GL_CULL_FACE);
672
673   glDisable(GL_DEPTH_TEST);
674   glBegin(GL_QUADS);
675
676   /* only draw white squares */
677   for(i = 0; i < BOARDSIZE; ++i) {
678     for(j = (BOARDSIZE+i) % 2; j < BOARDSIZE; j += 2) {
679       glVertex3f(i, 0.0, j + 1.0);
680       glVertex3f(i + 1.0, 0.0, j + 1.0);
681       glVertex3f(i + 1.0, 0.0, j);
682       glVertex3f(i, 0.0, j);
683       mi->polygon_count++;
684     }
685   }
686   glEnd();
687   glEnable(GL_DEPTH_TEST);
688
689   glColorMask(1, 1, 1, 1);
690   glStencilFunc(GL_EQUAL, 1, 1);
691   glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
692   
693   glPushMatrix(); 
694   glScalef(1.0, -1.0, 1.0);
695   glTranslatef(0.5, 0.0, 0.5);
696
697   glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
698   draw_pieces(mi, cs, cs->wire);
699   glPopMatrix();
700   
701   glDisable(GL_STENCIL_TEST);
702   glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
703
704   glEnable(GL_CULL_FACE);
705   glCullFace(GL_BACK);
706   glColorMask(1,1,1,1);
707 }
708
709 /** draws the scene */
710 static void display(ModeInfo *mi, Chesscreen *cs) 
711 {
712   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
713
714   mi->polygon_count = 0;
715
716   glMatrixMode(GL_MODELVIEW);
717   glLoadIdentity();
718   glRotatef(current_device_rotation(), 0, 0, 1);
719
720   /** setup perspectiv */
721   glTranslatef(0.0, 0.0, -1.5*BOARDSIZE);
722   glRotatef(30.0, 1.0, 0.0, 0.0);
723   gltrackball_rotate (cs->trackball);
724
725   if (rotate)
726     glRotatef(cs->theta*100, 0.0, 1.0, 0.0);
727   glTranslatef(-0.5*BOARDSIZE, 0.0, -0.5*BOARDSIZE);
728
729 /*   cs->position[0] = 4.0 + 1.0*-sin(cs->theta*100*M_PI/180.0); */
730 /*   cs->position[2] = 4.0 + 1.0* cos(cs->theta*100*M_PI/180.0); */
731 /*   cs->position[1] = 5.0; */
732
733   /* this is the lone light that the shadow matrix is generated from */
734   cs->position[0] = 1.0;
735   cs->position[2] = 1.0;
736   cs->position[1] = 16.0;
737
738   cs->position2[0] = 4.0 + 8.0*-sin(cs->theta*100*M_PI/180.0);
739   cs->position2[2] = 4.0 + 8.0* cos(cs->theta*100*M_PI/180.0);
740
741   if (!cs->wire) {
742     glEnable(GL_LIGHTING);
743     glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
744     glLightfv(GL_LIGHT1, GL_POSITION, cs->position2);
745     glEnable(GL_LIGHT0);
746   }
747
748   /** draw board, pieces */
749   if(!cs->wire) {
750     glEnable(GL_LIGHTING);
751     glEnable(GL_COLOR_MATERIAL);
752
753     if(reflections && !cs->wire) {
754       draw_reflections(mi, cs);
755       glEnable(GL_BLEND);
756     }
757
758     glEnable(GL_TEXTURE_2D);
759     glBindTexture(GL_TEXTURE_2D, cs->boardtexture);
760     drawBoard(mi, cs);
761     glDisable(GL_TEXTURE_2D);
762
763     if(shadows) {
764       /* render shadows */
765       GLfloat m[4][4];
766       shadowmatrix(m, cs->ground, cs->position);
767
768       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialShadow);
769       glEnable(GL_BLEND);
770       glDisable(GL_LIGHTING);
771       glDisable(GL_DEPTH_TEST);
772       
773       /* display ant shadow */
774       glPushMatrix();
775       glTranslatef(0.0, 0.001, 0.0);
776       glMultMatrixf(m[0]);
777       glTranslatef(0.5, 0.01, 0.5);
778       draw_shadow_pieces(mi, cs, cs->wire);
779       glPopMatrix();      
780
781       glEnable(GL_LIGHTING);
782       glDisable(GL_BLEND);
783       glEnable(GL_DEPTH_TEST);
784     }
785
786     if(reflections)
787       glDisable(GL_BLEND);
788   }
789   else
790     drawBoard(mi, cs);
791  
792   glTranslatef(0.5, 0.0, 0.5);
793   draw_pieces(mi, cs, cs->wire);
794
795   if(!cs->wire) {
796     glDisable(GL_COLOR_MATERIAL);
797     glDisable(GL_LIGHTING);
798   }
799
800   if (!cs->button_down_p)
801     cs->theta += .002;
802 }
803
804 /** reshape handler */
805 ENTRYPOINT void reshape_chess(ModeInfo *mi, int width, int height) 
806 {
807   GLfloat h = (GLfloat) height / (GLfloat) width;
808   glViewport(0,0, width, height);
809   glMatrixMode(GL_PROJECTION);
810   glLoadIdentity();
811   gluPerspective(45, 1/h, 2.0, 30.0);
812   glMatrixMode(GL_MODELVIEW);
813 }
814
815 /** initialization handler */
816 ENTRYPOINT void init_chess(ModeInfo *mi) 
817 {
818   Chesscreen *cs;
819   int screen = MI_SCREEN(mi);
820
821   if(!qs && 
822      !(qs = (Chesscreen *) calloc(MI_NUM_SCREENS(mi), sizeof(Chesscreen))))
823     return;
824   
825   cs = &qs[screen];
826   cs->window = MI_WINDOW(mi);
827   cs->wire = MI_IS_WIREFRAME(mi);
828   cs->trackball = gltrackball_init ();
829   
830   cs->oldwhite = -1;
831
832   cs->colors[0][0] = 1.0;
833   cs->colors[0][1] = 0.5;
834   cs->colors[0][2] = 0.0;
835
836   cs->colors[1][0] = 0.6;
837   cs->colors[1][1] = 0.6;
838   cs->colors[1][2] = 0.6;
839
840   cs->done = 1;
841   cs->count = 99;
842   cs->mod = 1.4;
843
844 /*   cs->position[0] = 0.0; */
845 /*   cs->position[1] = 5.0; */
846 /*   cs->position[2] = 5.0; */
847 /*   cs->position[3] = 1.0; */
848
849   cs->position[0] = 0.0;
850   cs->position[1] = 24.0;
851   cs->position[2] = 2.0;
852   cs->position[3] = 1.0;
853
854
855   cs->position2[0] = 5.0;
856   cs->position2[1] = 5.0;
857   cs->position2[2] = 5.0;
858   cs->position2[3] = 1.0;
859
860   cs->ground[0] = 0.0;
861   cs->ground[1] = 1.0;
862   cs->ground[2] = 0.0;
863   cs->ground[3] = -0.00001;
864
865   cs->oldgame = -1;
866
867
868   if((cs->glx_context = init_GL(mi)))
869     reshape_chess(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
870   else
871     MI_CLEARWINDOW(mi);
872
873   if (!cs->wire) {
874     glDepthFunc(GL_LEQUAL);
875     glClearStencil(0);
876     glEnable(GL_CULL_FACE);
877     glCullFace(GL_BACK);
878
879     make_piece_texture(cs);
880     make_board_texture(cs);
881   }
882   chessmodels_gen_lists( classic, cs->poly_counts);
883
884   if(!cs->wire) {
885     setup_lights(cs);
886     glColorMaterial(GL_FRONT, GL_DIFFUSE);
887     glShadeModel(smooth ? GL_SMOOTH : GL_FLAT);
888     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
889     glEnable(GL_DEPTH_TEST);
890   }
891   else
892     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
893 }
894
895 /** does dirty work drawing scene, moving pieces */
896 ENTRYPOINT void draw_chess(ModeInfo *mi) 
897 {
898   Chesscreen *cs = &qs[MI_SCREEN(mi)];
899   Window w = MI_WINDOW(mi);
900   Display *disp = MI_DISPLAY(mi);
901
902   if(!cs->glx_context)
903     return;
904
905   glXMakeCurrent(disp, w, *(cs->glx_context));
906
907   /** code for moving a piece */
908   if(cs->moving && ++cs->steps == 100) {
909     cs->moving = cs->count = cs->steps = cs->take = 0;
910     cs->game.board[cs->game.moves[cs->mc][2]][cs->game.moves[cs->mc][3]] = cs->mpiece;
911     ++cs->mc;
912     
913     if(cs->mc == cs->game.movecount) {
914       cs->done = 1;
915       cs->mc = 0;
916     }
917   }
918
919   if(++cs->count == 100) {
920     if(!cs->done) {
921       cs->mpiece = cs->game.board[cs->game.moves[cs->mc][0]][cs->game.moves[cs->mc][1]];
922       cs->game.board[cs->game.moves[cs->mc][0]][cs->game.moves[cs->mc][1]] = NONE;
923       
924       if((cs->tpiece = cs->game.board[cs->game.moves[cs->mc][2]][cs->game.moves[cs->mc][3]])) {
925         cs->game.board[cs->game.moves[cs->mc][2]][cs->game.moves[cs->mc][3]] = NONE;
926         cs->take = 1;
927       }
928       
929       cs->from[0] = cs->game.moves[cs->mc][0];
930       cs->from[1] = cs->game.moves[cs->mc][1];
931       cs->to[0] = cs->game.moves[cs->mc][2];
932       cs->to[1] = cs->game.moves[cs->mc][3];
933       
934       cs->dz = (cs->to[0] - cs->from[0]) / 100;
935       cs->dx = (cs->to[1] - cs->from[1]) / 100;
936       cs->steps = 0;
937       cs->moving = 1;
938     }
939     else if(cs->done == 1) {
940       int newgame = cs->oldgame;
941       while(newgame == cs->oldgame)
942         newgame = random()%GAMES;
943
944       /* mod the mod */
945       cs->mod = 0.6 + (random()%20)/10.0;
946
947       /* same old game */
948       cs->oldgame = newgame;
949       cs->game = games[cs->oldgame];
950       build_colors(cs);
951       cs->done = 2;
952       cs->count = 0;
953     }
954     else {
955       cs->done = 0;
956       cs->count = 0;
957     }
958   }
959
960   /* set lighting */
961   if(cs->done) {
962     glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 
963              cs->done == 1 ? 1.0+0.1*cs->count : 100.0/cs->count);
964     glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 
965              cs->done == 1 ? 1.0+0.1*cs->count : 100.0/cs->count);
966     glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.14);
967     glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.14);
968   }
969
970   display(mi, cs);
971
972   if(mi->fps_p) do_fps(mi);
973   glFinish(); 
974   glXSwapBuffers(disp, w);
975 }
976
977 /** bust it */
978 ENTRYPOINT void release_chess(ModeInfo *mi) 
979 {
980   if(qs)
981     free((void *) qs);
982   qs = 0;
983
984   FreeAllGL(mi);
985 }
986
987 XSCREENSAVER_MODULE_2 ("Endgame", endgame, chess)
988
989 #endif