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