From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[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 (gltrackball_event_handler (event, cs->trackball,
208                                  MI_WIDTH (mi), MI_HEIGHT (mi),
209                                  &cs->button_down_p))
210     return True;
211   else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
212     {
213       cs->done = 1;
214       return True;
215     }
216  
217   return False;
218 }
219
220 static const GLfloat diffuse2[] = {1.0, 1.0, 1.0, 1.0};
221 /*static const GLfloat ambient2[] = {0.7, 0.7, 0.7, 1.0};*/
222 static const GLfloat shininess[] = {60.0};
223 static const GLfloat specular[] = {0.4, 0.4, 0.4, 1.0};
224
225 /* configure lighting */
226 static void setup_lights(Chesscreen *cs) 
227 {
228   glEnable(GL_LIGHTING);
229   glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
230   glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse2);
231   glEnable(GL_LIGHT0);
232
233 /*   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient2); */
234
235   glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
236   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
237
238   glLightfv(GL_LIGHT1, GL_SPECULAR, diffuse2);
239   glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse2);
240   glEnable(GL_LIGHT1);
241 }
242
243 /* draw pieces */
244 static void drawPieces(ModeInfo *mi, Chesscreen *cs) 
245 {
246   int i, j;
247
248   for(i = 0; i < BOARDSIZE; ++i) {
249     for(j = 0; j < BOARDSIZE; ++j) {
250       if(cs->game.board[i][j]) {        
251         int c = cs->game.board[i][j]/PIECES;
252         glColor3fv(cs->colors[c]);
253         glCallList(cs->game.board[i][j]%PIECES);
254         mi->polygon_count += cs->poly_counts[cs->game.board[i][j]%PIECES];
255       }
256       
257       glTranslatef(1.0, 0.0, 0.0);
258     }
259     
260     glTranslatef(-1.0*BOARDSIZE, 0.0, 1.0);
261   }
262
263   glTranslatef(0.0, 0.0, -1.0*BOARDSIZE);
264 }
265
266 /* draw pieces */
267 static void drawPiecesShadow(ModeInfo *mi, Chesscreen *cs) 
268 {
269   int i, j;
270
271   for(i = 0; i < BOARDSIZE; ++i) {
272     for(j = 0; j < BOARDSIZE; ++j) {
273       if(cs->game.board[i][j]) {        
274         glColor4f(0.0, 0.0, 0.0, 0.4);
275         glCallList(cs->game.board[i][j]%PIECES);
276         mi->polygon_count += cs->poly_counts[cs->game.board[i][j]%PIECES];
277       }
278       
279       glTranslatef(1.0, 0.0, 0.0);
280     }
281     
282     glTranslatef(-1.0*BOARDSIZE, 0.0, 1.0);
283   }
284
285   glTranslatef(0.0, 0.0, -1.0*BOARDSIZE);
286 }
287
288 /* draw a moving piece */
289 static void drawMovingPiece(ModeInfo *mi, Chesscreen *cs, int shadow) 
290 {
291   int piece = cs->mpiece % PIECES;
292
293   if (piece == NONE) return;
294
295   glPushMatrix();
296
297   if(shadow) glColor4fv(MaterialShadow);
298   else glColor3fv(cs->colors[cs->mpiece/PIECES]);
299
300   /** assume a queening.  should be more general */
301   if((cs->mpiece == PAWN  && fabs(cs->to[0]) < 0.01) || 
302      (cs->mpiece == BPAWN && fabs(cs->to[0]-7.0) < 0.01)) {
303     glTranslatef(cs->from[1]+cs->steps*cs->dx, 0.0, cs->from[0]+cs->steps*cs->dz);
304
305     glColor4f(shadow ? MaterialShadow[0] : cs->colors[cs->mpiece/7][0], 
306               shadow ? MaterialShadow[1] : cs->colors[cs->mpiece/7][1], 
307               shadow ? MaterialShadow[2] : cs->colors[cs->mpiece/7][2],
308               (fabs(50.0-cs->steps))/50.0);
309
310     piece = cs->steps < 50 ? PAWN : QUEEN;
311
312     /* what a kludge */
313     if(cs->steps == 99)
314       cs->mpiece = cs->mpiece == PAWN ? QUEEN : BQUEEN;
315   }
316   else if(cs->mpiece % PIECES == KNIGHT) {
317     /* If there is nothing in the path of a knight, move it by sliding,
318        just like the other pieces.  But if there are any pieces on the
319        middle two squares in its path, the knight would intersect them,
320        so in that case, move it in an airborne arc. */
321     GLfloat y;
322     int i, j;
323     Bool blocked_p = False;
324     int fromx = MIN(cs->from[1], cs->to[1]);
325     int fromy = MIN(cs->from[0], cs->to[0]);
326     int tox   = MAX(cs->from[1], cs->to[1]);
327     int toy   = MAX(cs->from[0], cs->to[0]);
328     if (fromx == tox-2) fromx = tox = fromx+1;
329     if (fromy == toy-2) fromy = toy = fromy+1;
330     for (i = fromy; i <= toy; i++) {
331       for (j = fromx; j <= tox; j++) {
332         if (cs->game.board[i][j]) {
333           blocked_p = True;
334           break;
335         }
336       }
337     }
338
339     if (!blocked_p)
340       goto SLIDE;
341
342     /* Move by hopping. */
343     y = 1.5 * sin (M_PI * cs->steps / 100.0);
344     glTranslatef(cs->from[1]+cs->steps*cs->dx, y,
345                  cs->from[0]+cs->steps*cs->dz);
346
347   } else {
348   SLIDE:
349     /* Move by sliding. */
350     glTranslatef(cs->from[1]+cs->steps*cs->dx, 0.0, cs->from[0]+cs->steps*cs->dz);
351   }
352
353
354   if(!cs->wire)
355     glEnable(GL_BLEND);
356   
357   glCallList(piece);
358   mi->polygon_count += cs->poly_counts[cs->mpiece % PIECES];
359
360   glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
361   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
362
363   glPopMatrix();
364
365   if(!cs->wire)
366     glDisable(GL_BLEND);
367 }
368
369 /** code to squish a taken piece */
370 static void drawTakePiece(ModeInfo *mi, Chesscreen *cs, int shadow) 
371 {
372   if(!cs->wire)
373     glEnable(GL_BLEND);
374
375   glColor4f(shadow ? MaterialShadow[0] : cs->colors[cs->tpiece/7][0], 
376             shadow ? MaterialShadow[1] : cs->colors[cs->tpiece/7][1], 
377             shadow ? MaterialShadow[2] : cs->colors[cs->tpiece/7][2],
378             (100-1.6*cs->steps)/100.0);
379
380   glTranslatef(cs->to[1], 0.0, cs->to[0]);
381   
382   if(cs->mpiece % PIECES == KNIGHT)
383     glScalef(1.0+cs->steps/100.0, 1.0, 1.0+cs->steps/100.0);
384   else
385     glScalef(1.0, 1 - cs->steps/50.0 > 0.01 ? 1 - cs->steps/50.0 : 0.01, 1.0);
386   glCallList(cs->tpiece % 7);
387   mi->polygon_count += cs->poly_counts[cs->tpiece % PIECES];
388   
389   if(!cs->wire)
390     glDisable(GL_BLEND);
391 }
392
393 /** draw board */
394 static void drawBoard(ModeInfo *mi, Chesscreen *cs) 
395 {
396   int i, j;
397
398   glBegin(GL_QUADS);
399
400   for(i = 0; i < BOARDSIZE; ++i)
401     for(j = 0; j < BOARDSIZE; ++j) {
402       double ma1 = (i+j)%2 == 0 ? cs->mod*i : 0.0;
403       double mb1 = (i+j)%2 == 0 ? cs->mod*j : 0.0;
404       double ma2 = (i+j)%2 == 0 ? cs->mod*(i+1.0) : 0.01;
405       double mb2 = (i+j)%2 == 0 ? cs->mod*(j+1.0) : 0.01;
406
407       /*glColor3fv(colors[(i+j)%2]);*/
408       glColor4f(cs->colors[(i+j)%2][0], cs->colors[(i+j)%2][1],
409                 cs->colors[(i+j)%2][2], 0.65);
410       
411       glNormal3f(0.0, 1.0, 0.0);
412 /*       glTexCoord2f(mod*i, mod*(j+1.0)); */
413       glTexCoord2f(ma1, mb2);
414       glVertex3f(i, 0.0, j + 1.0);
415 /*       glTexCoord2f(mod*(i+1.0), mod*(j+1.0)); */
416       glTexCoord2f(ma2, mb2);
417       glVertex3f(i + 1.0, 0.0, j + 1.0);
418       glTexCoord2f(ma2, mb1);
419 /*       glTexCoord2f(mod*(i+1.0), mod*j); */
420       glVertex3f(i + 1.0, 0.0, j);
421       glTexCoord2f(ma1, mb1);
422 /*       glTexCoord2f(mod*i, mod*j); */
423       glVertex3f(i, 0.0, j);
424
425       mi->polygon_count++;
426     }
427   glEnd();
428
429   {
430     GLfloat off = 0.01;
431     GLfloat w = BOARDSIZE;
432     GLfloat h = 0.1;
433
434     /* Give the board a slight lip. */
435     /* #### oops, normals are wrong here, but you can't tell */
436
437     glColor3f(0.3, 0.3, 0.3);
438     glBegin (GL_QUADS);
439     glVertex3f (0,  0, 0);
440     glVertex3f (0, -h, 0);
441     glVertex3f (0, -h, w);
442     glVertex3f (0,  0, w);
443
444     glVertex3f (0,  0, w);
445     glVertex3f (0, -h, w);
446     glVertex3f (w, -h, w);
447     glVertex3f (w,  0, w);
448
449     glVertex3f (w,  0, w);
450     glVertex3f (w, -h, w);
451     glVertex3f (w, -h, 0);
452     glVertex3f (w,  0, 0);
453
454     glVertex3f (w,  0, 0);
455     glVertex3f (w, -h, 0);
456     glVertex3f (0, -h, 0);
457     glVertex3f (0,  0, 0);
458
459     glVertex3f (0, -h, 0);
460     glVertex3f (w, -h, 0);
461     glVertex3f (w, -h, w);
462     glVertex3f (0, -h, w);
463     glEnd();
464     mi->polygon_count += 4;
465
466     /* Fill in the underside of the board with an invisible black box
467        to hide the reflections that are not on tiles.  Probably there's
468        a way to do this with stencils instead.
469      */
470     w -= off*2;
471     h = 5;
472
473     glPushMatrix();
474     glTranslatef (off, 0, off);
475     glDisable(GL_LIGHTING);
476     glColor3f(0,0,0);
477     glBegin (GL_QUADS);
478     glVertex3f (0,  0, 0);
479     glVertex3f (0, -h, 0);
480     glVertex3f (0, -h, w);
481     glVertex3f (0,  0, w);
482
483     glVertex3f (0,  0, w);
484     glVertex3f (0, -h, w);
485     glVertex3f (w, -h, w);
486     glVertex3f (w,  0, w);
487
488     glVertex3f (w,  0, w);
489     glVertex3f (w, -h, w);
490     glVertex3f (w, -h, 0);
491     glVertex3f (w,  0, 0);
492
493     glVertex3f (w,  0, 0);
494     glVertex3f (w, -h, 0);
495     glVertex3f (0, -h, 0);
496     glVertex3f (0,  0, 0);
497
498     glVertex3f (0, -h, 0);
499     glVertex3f (w, -h, 0);
500     glVertex3f (w, -h, w);
501     glVertex3f (0, -h, w);
502     glEnd();
503     mi->polygon_count += 4;
504     glPopMatrix();
505     if (!cs->wire)
506       glEnable(GL_LIGHTING);
507   }
508 }
509
510 static void draw_pieces(ModeInfo *mi, Chesscreen *cs, int wire) 
511 {
512   if (!cs->wire) {
513     glEnable(GL_TEXTURE_2D);
514     glBindTexture(GL_TEXTURE_2D, cs->piecetexture);
515     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
516
517     glColor4f(0.5, 0.5, 0.5, 1.0);
518     glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialShadow);
519   }
520
521   drawPieces(mi, cs);
522   if(cs->moving) drawMovingPiece(mi, cs, 0);
523   if(cs->take) drawTakePiece(mi, cs, 0);
524   glDisable(GL_TEXTURE_2D);
525 }
526
527 static void draw_shadow_pieces(ModeInfo *mi, Chesscreen *cs, int wire) 
528 {
529   if (!cs->wire) {
530     glEnable(GL_TEXTURE_2D);
531     glBindTexture(GL_TEXTURE_2D, cs->piecetexture);
532     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
533   }
534
535   /* use the stencil */
536   glDisable(GL_LIGHTING);
537   glDisable(GL_COLOR_MATERIAL);
538   glDisable(GL_DEPTH_TEST);
539   glDisable(GL_TEXTURE_2D);
540   glDisable(GL_BLEND);
541
542   glClear(GL_STENCIL_BUFFER_BIT);
543   glColorMask(0,0,0,0);
544   glEnable(GL_STENCIL_TEST);
545
546   glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFFL);
547   glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
548   
549
550   glPushMatrix();
551   glTranslatef(0.0, 0.001, 0.0);
552
553   /* draw the pieces */
554   drawPiecesShadow(mi, cs);
555   if(cs->moving) drawMovingPiece(mi, cs, shadows);
556   if(cs->take) drawTakePiece(mi, cs, shadows);
557
558   glPopMatrix();
559
560
561   /* turn on drawing into colour buffer */
562   glColorMask(1,1,1,1);
563
564   /* programming with effect */
565   glDisable(GL_LIGHTING);
566   glDisable(GL_COLOR_MATERIAL);
567   glDisable(GL_TEXTURE_2D);
568
569   /* now draw the union of the shadows */
570
571   /* 
572      <todo>
573      want to keep alpha values (alpha is involved in transition
574      effects of the active pieces).
575      </todo>
576   */
577   glStencilFunc(GL_NOTEQUAL, 0, 0xFFFFFFFFL);
578   glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
579
580   glEnable(GL_BLEND);
581
582   glColor4fv(MaterialShadow);
583
584   /* draw the board generously to fill the shadows */
585   glBegin(GL_QUADS);
586
587   glVertex3f(-1.0, 0.0, -1.0);
588   glVertex3f(-1.0, 0.0, BOARDSIZE + 1.0);
589   glVertex3f(1.0 + BOARDSIZE, 0.0, BOARDSIZE + 1.0);
590   glVertex3f(1.0 + BOARDSIZE, 0.0, -1.0);
591
592   glEnd();
593
594   glDisable(GL_STENCIL_TEST);
595
596   /* "pop" attributes */
597   glEnable(GL_TEXTURE_2D);
598   glEnable(GL_COLOR_MATERIAL);
599   glEnable(GL_LIGHTING);
600   glEnable(GL_CULL_FACE);
601
602
603
604 }
605
606 enum {X, Y, Z, W};
607 enum {A, B, C, D};
608
609 /* create a matrix that will project the desired shadow */
610 static void shadowmatrix(GLfloat shadowMat[4][4],
611                   GLfloat groundplane[4],
612                   GLfloat lightpos[4]) 
613 {
614   GLfloat dot;
615
616   /* find dot product between light position vector and ground plane normal */
617   dot = groundplane[X] * lightpos[X] +
618         groundplane[Y] * lightpos[Y] +
619         groundplane[Z] * lightpos[Z] +
620         groundplane[W] * lightpos[W];
621
622   shadowMat[0][0] = dot - lightpos[X] * groundplane[X];
623   shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y];
624   shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z];
625   shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W];
626
627   shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X];
628   shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y];
629   shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z];
630   shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W];
631
632   shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X];
633   shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y];
634   shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z];
635   shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W];
636
637   shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X];
638   shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y];
639   shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z];
640   shadowMat[3][3] = dot - lightpos[W] * groundplane[W];
641 }
642
643 /** reflectionboard */
644 static void draw_reflections(ModeInfo *mi, Chesscreen *cs) 
645 {
646   int i, j;
647
648   glEnable(GL_STENCIL_TEST);
649   glStencilFunc(GL_ALWAYS, 1, 1);
650   glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
651   glColorMask(0,0,0,0);
652   glDisable(GL_CULL_FACE);
653
654   glDisable(GL_DEPTH_TEST);
655   glBegin(GL_QUADS);
656
657   /* only draw white squares */
658   for(i = 0; i < BOARDSIZE; ++i) {
659     for(j = (BOARDSIZE+i) % 2; j < BOARDSIZE; j += 2) {
660       glVertex3f(i, 0.0, j + 1.0);
661       glVertex3f(i + 1.0, 0.0, j + 1.0);
662       glVertex3f(i + 1.0, 0.0, j);
663       glVertex3f(i, 0.0, j);
664       mi->polygon_count++;
665     }
666   }
667   glEnd();
668   glEnable(GL_DEPTH_TEST);
669
670   glColorMask(1, 1, 1, 1);
671   glStencilFunc(GL_EQUAL, 1, 1);
672   glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
673   
674   glPushMatrix(); 
675   glScalef(1.0, -1.0, 1.0);
676   glTranslatef(0.5, 0.0, 0.5);
677
678   glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
679   draw_pieces(mi, cs, cs->wire);
680   glPopMatrix();
681   
682   glDisable(GL_STENCIL_TEST);
683   glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
684
685   glEnable(GL_CULL_FACE);
686   glCullFace(GL_BACK);
687   glColorMask(1,1,1,1);
688 }
689
690 /** draws the scene */
691 static void display(ModeInfo *mi, Chesscreen *cs) 
692 {
693   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
694
695   mi->polygon_count = 0;
696
697   glMatrixMode(GL_MODELVIEW);
698   glLoadIdentity();
699   glRotatef(current_device_rotation(), 0, 0, 1);
700
701   /** setup perspectiv */
702   glTranslatef(0.0, 0.0, -1.5*BOARDSIZE);
703   glRotatef(30.0, 1.0, 0.0, 0.0);
704   gltrackball_rotate (cs->trackball);
705
706   if (rotate)
707     glRotatef(cs->theta*100, 0.0, 1.0, 0.0);
708   glTranslatef(-0.5*BOARDSIZE, 0.0, -0.5*BOARDSIZE);
709
710 /*   cs->position[0] = 4.0 + 1.0*-sin(cs->theta*100*M_PI/180.0); */
711 /*   cs->position[2] = 4.0 + 1.0* cos(cs->theta*100*M_PI/180.0); */
712 /*   cs->position[1] = 5.0; */
713
714   /* this is the lone light that the shadow matrix is generated from */
715   cs->position[0] = 1.0;
716   cs->position[2] = 1.0;
717   cs->position[1] = 16.0;
718
719   cs->position2[0] = 4.0 + 8.0*-sin(cs->theta*100*M_PI/180.0);
720   cs->position2[2] = 4.0 + 8.0* cos(cs->theta*100*M_PI/180.0);
721
722   if (!cs->wire) {
723     glEnable(GL_LIGHTING);
724     glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
725     glLightfv(GL_LIGHT1, GL_POSITION, cs->position2);
726     glEnable(GL_LIGHT0);
727   }
728
729   /** draw board, pieces */
730   if(!cs->wire) {
731     glEnable(GL_LIGHTING);
732     glEnable(GL_COLOR_MATERIAL);
733
734     if(reflections && !cs->wire) {
735       draw_reflections(mi, cs);
736       glEnable(GL_BLEND);
737     }
738
739     glEnable(GL_TEXTURE_2D);
740     glBindTexture(GL_TEXTURE_2D, cs->boardtexture);
741     drawBoard(mi, cs);
742     glDisable(GL_TEXTURE_2D);
743
744     if(shadows) {
745       /* render shadows */
746       GLfloat m[4][4];
747       shadowmatrix(m, cs->ground, cs->position);
748
749       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialShadow);
750       glEnable(GL_BLEND);
751       glDisable(GL_LIGHTING);
752       glDisable(GL_DEPTH_TEST);
753       
754       /* display ant shadow */
755       glPushMatrix();
756       glTranslatef(0.0, 0.001, 0.0);
757       glMultMatrixf(m[0]);
758       glTranslatef(0.5, 0.01, 0.5);
759       draw_shadow_pieces(mi, cs, cs->wire);
760       glPopMatrix();      
761
762       glEnable(GL_LIGHTING);
763       glDisable(GL_BLEND);
764       glEnable(GL_DEPTH_TEST);
765     }
766
767     if(reflections)
768       glDisable(GL_BLEND);
769   }
770   else
771     drawBoard(mi, cs);
772  
773   glTranslatef(0.5, 0.0, 0.5);
774   draw_pieces(mi, cs, cs->wire);
775
776   if(!cs->wire) {
777     glDisable(GL_COLOR_MATERIAL);
778     glDisable(GL_LIGHTING);
779   }
780
781   if (!cs->button_down_p)
782     cs->theta += .002;
783 }
784
785 /** reshape handler */
786 ENTRYPOINT void reshape_chess(ModeInfo *mi, int width, int height) 
787 {
788   GLfloat h = (GLfloat) height / (GLfloat) width;
789   glViewport(0,0, width, height);
790   glMatrixMode(GL_PROJECTION);
791   glLoadIdentity();
792   gluPerspective(45, 1/h, 2.0, 30.0);
793   glMatrixMode(GL_MODELVIEW);
794 }
795
796 /** initialization handler */
797 ENTRYPOINT void init_chess(ModeInfo *mi) 
798 {
799   Chesscreen *cs;
800   int screen = MI_SCREEN(mi);
801
802   if(!qs && 
803      !(qs = (Chesscreen *) calloc(MI_NUM_SCREENS(mi), sizeof(Chesscreen))))
804     return;
805   
806   cs = &qs[screen];
807   cs->window = MI_WINDOW(mi);
808   cs->wire = MI_IS_WIREFRAME(mi);
809   cs->trackball = gltrackball_init (False);
810   
811   cs->oldwhite = -1;
812
813   cs->colors[0][0] = 1.0;
814   cs->colors[0][1] = 0.5;
815   cs->colors[0][2] = 0.0;
816
817   cs->colors[1][0] = 0.6;
818   cs->colors[1][1] = 0.6;
819   cs->colors[1][2] = 0.6;
820
821   cs->done = 1;
822   cs->count = 99;
823   cs->mod = 1.4;
824
825 /*   cs->position[0] = 0.0; */
826 /*   cs->position[1] = 5.0; */
827 /*   cs->position[2] = 5.0; */
828 /*   cs->position[3] = 1.0; */
829
830   cs->position[0] = 0.0;
831   cs->position[1] = 24.0;
832   cs->position[2] = 2.0;
833   cs->position[3] = 1.0;
834
835
836   cs->position2[0] = 5.0;
837   cs->position2[1] = 5.0;
838   cs->position2[2] = 5.0;
839   cs->position2[3] = 1.0;
840
841   cs->ground[0] = 0.0;
842   cs->ground[1] = 1.0;
843   cs->ground[2] = 0.0;
844   cs->ground[3] = -0.00001;
845
846   cs->oldgame = -1;
847
848
849   if((cs->glx_context = init_GL(mi)))
850     reshape_chess(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
851   else
852     MI_CLEARWINDOW(mi);
853
854   if (!cs->wire) {
855     glDepthFunc(GL_LEQUAL);
856     glClearStencil(0);
857     glEnable(GL_CULL_FACE);
858     glCullFace(GL_BACK);
859
860     make_piece_texture(cs);
861     make_board_texture(cs);
862   }
863   chessmodels_gen_lists( classic, cs->poly_counts);
864
865 # ifdef HAVE_JWZGLES /* #### glPolygonMode other than GL_FILL unimplemented */
866     cs->wire = 0;
867 # endif
868
869   if(!cs->wire) {
870     setup_lights(cs);
871     glColorMaterial(GL_FRONT, GL_DIFFUSE);
872     glShadeModel(smooth ? GL_SMOOTH : GL_FLAT);
873     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
874     glEnable(GL_DEPTH_TEST);
875   }
876   else
877     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
878 }
879
880 /** does dirty work drawing scene, moving pieces */
881 ENTRYPOINT void draw_chess(ModeInfo *mi) 
882 {
883   Chesscreen *cs = &qs[MI_SCREEN(mi)];
884   Window w = MI_WINDOW(mi);
885   Display *disp = MI_DISPLAY(mi);
886
887   if(!cs->glx_context)
888     return;
889
890   glXMakeCurrent(disp, w, *(cs->glx_context));
891
892   /** code for moving a piece */
893   if(cs->moving && ++cs->steps == 100) {
894     cs->moving = cs->count = cs->steps = cs->take = 0;
895     cs->game.board[cs->game.moves[cs->mc][2]][cs->game.moves[cs->mc][3]] = cs->mpiece;
896     ++cs->mc;
897     
898     if(cs->mc == cs->game.movecount) {
899       cs->done = 1;
900       cs->mc = 0;
901     }
902   }
903
904   if(++cs->count == 100) {
905     if(!cs->done) {
906       cs->mpiece = cs->game.board[cs->game.moves[cs->mc][0]][cs->game.moves[cs->mc][1]];
907       cs->game.board[cs->game.moves[cs->mc][0]][cs->game.moves[cs->mc][1]] = NONE;
908       
909       if((cs->tpiece = cs->game.board[cs->game.moves[cs->mc][2]][cs->game.moves[cs->mc][3]])) {
910         cs->game.board[cs->game.moves[cs->mc][2]][cs->game.moves[cs->mc][3]] = NONE;
911         cs->take = 1;
912       }
913       
914       cs->from[0] = cs->game.moves[cs->mc][0];
915       cs->from[1] = cs->game.moves[cs->mc][1];
916       cs->to[0] = cs->game.moves[cs->mc][2];
917       cs->to[1] = cs->game.moves[cs->mc][3];
918       
919       cs->dz = (cs->to[0] - cs->from[0]) / 100;
920       cs->dx = (cs->to[1] - cs->from[1]) / 100;
921       cs->steps = 0;
922       cs->moving = 1;
923     }
924     else if(cs->done == 1) {
925       int newgame = cs->oldgame;
926       while(newgame == cs->oldgame)
927         newgame = random()%GAMES;
928
929       /* mod the mod */
930       cs->mod = 0.6 + (random()%20)/10.0;
931
932       /* same old game */
933       cs->oldgame = newgame;
934       cs->game = games[cs->oldgame];
935       build_colors(cs);
936       cs->done = 2;
937       cs->count = 0;
938     }
939     else {
940       cs->done = 0;
941       cs->count = 0;
942     }
943   }
944
945   /* set lighting */
946   if(cs->done) {
947     glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 
948              cs->done == 1 ? 1.0+0.1*cs->count : 100.0/cs->count);
949     glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 
950              cs->done == 1 ? 1.0+0.1*cs->count : 100.0/cs->count);
951     glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.14);
952     glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.14);
953   }
954
955   display(mi, cs);
956
957   if(mi->fps_p) do_fps(mi);
958   glFinish(); 
959   glXSwapBuffers(disp, w);
960 }
961
962 /** bust it */
963 ENTRYPOINT void release_chess(ModeInfo *mi) 
964 {
965   if(qs)
966     free((void *) qs);
967   qs = 0;
968
969   FreeAllGL(mi);
970 }
971
972 XSCREENSAVER_MODULE_2 ("Endgame", endgame, chess)
973
974 #endif