From http://www.jwz.org/xscreensaver/xscreensaver-5.16.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(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     GLfloat shine[1];
337     GLfloat spec[4];
338     GLfloat mult;
339     glTranslatef(cs->steps < 50 ? cs->from[1] : cs->to[1], 0.0, 
340                  cs->steps < 50 ? cs->from[0] : cs->to[0]);
341
342     mult = cs->steps < 10 
343       ? (1.0 - cs->steps / 10.0) : 100 - cs->steps < 10 
344       ? (1.0 - (100 - cs->steps) / 10.0) : 0.0;
345
346     shine[0] = mult*shininess[0];
347     spec[0] = mult*specular[0];
348     spec[1] = mult*specular[1];
349     spec[2] = mult*specular[2];
350     spec[3] = 1.0;
351     glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shine);
352     glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spec);
353
354     glColor4f(shadow ? MaterialShadow[0] : cs->colors[cs->mpiece/7][0], 
355               shadow ? MaterialShadow[1] : cs->colors[cs->mpiece/7][1], 
356               shadow ? MaterialShadow[2] : cs->colors[cs->mpiece/7][2],
357               fabs(49-cs->steps)/49.0);
358
359     glScalef(fabs(49-cs->steps)/49.0, fabs(49-cs->steps)/49.0, fabs(49-cs->steps)/49.0);
360   }
361   else
362     glTranslatef(cs->from[1]+cs->steps*cs->dx, 0.0, cs->from[0]+cs->steps*cs->dz);
363
364   if(!cs->wire)
365     glEnable(GL_BLEND);
366   
367   glCallList(piece);
368   mi->polygon_count += cs->poly_counts[cs->mpiece % PIECES];
369
370   glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
371   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
372
373   glPopMatrix();
374
375   if(!cs->wire)
376     glDisable(GL_BLEND);
377 }
378
379 /** code to squish a taken piece */
380 static void drawTakePiece(ModeInfo *mi, Chesscreen *cs, int shadow) 
381 {
382   if(!cs->wire)
383     glEnable(GL_BLEND);
384
385   glColor4f(shadow ? MaterialShadow[0] : cs->colors[cs->tpiece/7][0], 
386             shadow ? MaterialShadow[1] : cs->colors[cs->tpiece/7][1], 
387             shadow ? MaterialShadow[2] : cs->colors[cs->tpiece/7][2],
388             (100-1.6*cs->steps)/100.0);
389
390   glTranslatef(cs->to[1], 0.0, cs->to[0]);
391   
392   if(cs->mpiece % PIECES == KNIGHT)
393     glScalef(1.0+cs->steps/100.0, 1.0, 1.0+cs->steps/100.0);
394   else
395     glScalef(1.0, 1 - cs->steps/50.0 > 0.01 ? 1 - cs->steps/50.0 : 0.01, 1.0);
396   glCallList(cs->tpiece % 7);
397   mi->polygon_count += cs->poly_counts[cs->tpiece % PIECES];
398   
399   if(!cs->wire)
400     glDisable(GL_BLEND);
401 }
402
403 /** draw board */
404 static void drawBoard(ModeInfo *mi, Chesscreen *cs) 
405 {
406   int i, j;
407
408   glBegin(GL_QUADS);
409
410   for(i = 0; i < BOARDSIZE; ++i)
411     for(j = 0; j < BOARDSIZE; ++j) {
412       double ma1 = (i+j)%2 == 0 ? cs->mod*i : 0.0;
413       double mb1 = (i+j)%2 == 0 ? cs->mod*j : 0.0;
414       double ma2 = (i+j)%2 == 0 ? cs->mod*(i+1.0) : 0.01;
415       double mb2 = (i+j)%2 == 0 ? cs->mod*(j+1.0) : 0.01;
416
417       /*glColor3fv(colors[(i+j)%2]);*/
418       glColor4f(cs->colors[(i+j)%2][0], cs->colors[(i+j)%2][1],
419                 cs->colors[(i+j)%2][2], 0.65);
420       
421       glNormal3f(0.0, 1.0, 0.0);
422 /*       glTexCoord2f(mod*i, mod*(j+1.0)); */
423       glTexCoord2f(ma1, mb2);
424       glVertex3f(i, 0.0, j + 1.0);
425 /*       glTexCoord2f(mod*(i+1.0), mod*(j+1.0)); */
426       glTexCoord2f(ma2, mb2);
427       glVertex3f(i + 1.0, 0.0, j + 1.0);
428       glTexCoord2f(ma2, mb1);
429 /*       glTexCoord2f(mod*(i+1.0), mod*j); */
430       glVertex3f(i + 1.0, 0.0, j);
431       glTexCoord2f(ma1, mb1);
432 /*       glTexCoord2f(mod*i, mod*j); */
433       glVertex3f(i, 0.0, j);
434
435       mi->polygon_count++;
436     }
437
438   /* chop underneath board */
439 /*   glColor3f(0, 0, 0); */
440 /*   glNormal3f(0, -1, 0); */
441 /*   glVertex3f(0,         0,  BOARDSIZE); */
442 /*   glVertex3f(0,         0,  0); */
443 /*   glVertex3f(BOARDSIZE, 0,  0); */
444 /*   glVertex3f(BOARDSIZE, 0,  BOARDSIZE); */
445   glEnd();
446 }
447
448 static void draw_pieces(ModeInfo *mi, Chesscreen *cs, int wire) 
449 {
450   if (!cs->wire) {
451     glEnable(GL_TEXTURE_2D);
452     glBindTexture(GL_TEXTURE_2D, cs->piecetexture);
453     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
454
455     glColor4f(0.5, 0.5, 0.5, 1.0);
456     glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialShadow);
457   }
458
459   drawPieces(mi, cs);
460   if(cs->moving) drawMovingPiece(mi, cs, 0);
461   if(cs->take) drawTakePiece(mi, cs, 0);
462   glDisable(GL_TEXTURE_2D);
463 }
464
465 static void draw_shadow_pieces(ModeInfo *mi, Chesscreen *cs, int wire) 
466 {
467   if (!cs->wire) {
468     glEnable(GL_TEXTURE_2D);
469     glBindTexture(GL_TEXTURE_2D, cs->piecetexture);
470     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
471   }
472
473   /* use the stencil */
474   glDisable(GL_LIGHTING);
475   glDisable(GL_COLOR_MATERIAL);
476   glDisable(GL_DEPTH_TEST);
477   glDisable(GL_TEXTURE_2D);
478   glDisable(GL_BLEND);
479
480   glClear(GL_STENCIL_BUFFER_BIT);
481   glColorMask(0,0,0,0);
482   glEnable(GL_STENCIL_TEST);
483
484   glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFFL);
485   glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
486   
487
488   glPushMatrix();
489   glTranslatef(0.0, 0.001, 0.0);
490
491   /* draw the pieces */
492   drawPiecesShadow(mi, cs);
493   if(cs->moving) drawMovingPiece(mi, cs, shadows);
494   if(cs->take) drawTakePiece(mi, cs, shadows);
495
496   glPopMatrix();
497
498
499   /* turn on drawing into colour buffer */
500   glColorMask(1,1,1,1);
501
502   /* programming with effect */
503   glDisable(GL_LIGHTING);
504   glDisable(GL_COLOR_MATERIAL);
505   glDisable(GL_TEXTURE_2D);
506
507   /* now draw the union of the shadows */
508
509   /* 
510      <todo>
511      want to keep alpha values (alpha is involved in transition
512      effects of the active pieces).
513      </todo>
514   */
515   glStencilFunc(GL_NOTEQUAL, 0, 0xFFFFFFFFL);
516   glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
517
518   glEnable(GL_BLEND);
519
520   glColor4fv(MaterialShadow);
521
522   /* draw the board generously to fill the shadows */
523   glBegin(GL_QUADS);
524
525   glVertex3f(-1.0, 0.0, -1.0);
526   glVertex3f(-1.0, 0.0, BOARDSIZE + 1.0);
527   glVertex3f(1.0 + BOARDSIZE, 0.0, BOARDSIZE + 1.0);
528   glVertex3f(1.0 + BOARDSIZE, 0.0, -1.0);
529
530   glEnd();
531
532   glDisable(GL_STENCIL_TEST);
533
534   /* "pop" attributes */
535   glEnable(GL_TEXTURE_2D);
536   glEnable(GL_COLOR_MATERIAL);
537   glEnable(GL_LIGHTING);
538   glEnable(GL_CULL_FACE);
539
540
541
542 }
543
544 enum {X, Y, Z, W};
545 enum {A, B, C, D};
546
547 /* create a matrix that will project the desired shadow */
548 static void shadowmatrix(GLfloat shadowMat[4][4],
549                   GLfloat groundplane[4],
550                   GLfloat lightpos[4]) 
551 {
552   GLfloat dot;
553
554   /* find dot product between light position vector and ground plane normal */
555   dot = groundplane[X] * lightpos[X] +
556         groundplane[Y] * lightpos[Y] +
557         groundplane[Z] * lightpos[Z] +
558         groundplane[W] * lightpos[W];
559
560   shadowMat[0][0] = dot - lightpos[X] * groundplane[X];
561   shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y];
562   shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z];
563   shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W];
564
565   shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X];
566   shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y];
567   shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z];
568   shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W];
569
570   shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X];
571   shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y];
572   shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z];
573   shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W];
574
575   shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X];
576   shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y];
577   shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z];
578   shadowMat[3][3] = dot - lightpos[W] * groundplane[W];
579 }
580
581 /** reflectionboard */
582 static void draw_reflections(ModeInfo *mi, Chesscreen *cs) 
583 {
584   int i, j;
585
586   glEnable(GL_STENCIL_TEST);
587   glStencilFunc(GL_ALWAYS, 1, 1);
588   glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
589   glColorMask(0,0,0,0);
590   glDisable(GL_CULL_FACE);
591
592   glDisable(GL_DEPTH_TEST);
593   glBegin(GL_QUADS);
594
595   /* only draw white squares */
596   for(i = 0; i < BOARDSIZE; ++i) {
597     for(j = (BOARDSIZE+i) % 2; j < BOARDSIZE; j += 2) {
598       glVertex3f(i, 0.0, j + 1.0);
599       glVertex3f(i + 1.0, 0.0, j + 1.0);
600       glVertex3f(i + 1.0, 0.0, j);
601       glVertex3f(i, 0.0, j);
602       mi->polygon_count++;
603     }
604   }
605   glEnd();
606   glEnable(GL_DEPTH_TEST);
607
608   glColorMask(1, 1, 1, 1);
609   glStencilFunc(GL_EQUAL, 1, 1);
610   glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
611   
612   glPushMatrix(); 
613   glScalef(1.0, -1.0, 1.0);
614   glTranslatef(0.5, 0.0, 0.5);
615
616   glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
617   draw_pieces(mi, cs, cs->wire);
618   glPopMatrix();
619   
620   glDisable(GL_STENCIL_TEST);
621   glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
622
623   glEnable(GL_CULL_FACE);
624   glCullFace(GL_BACK);
625   glColorMask(1,1,1,1);
626 }
627
628 /** draws the scene */
629 static void display(ModeInfo *mi, Chesscreen *cs) 
630 {
631   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
632
633   mi->polygon_count = 0;
634
635   glMatrixMode(GL_MODELVIEW);
636   glLoadIdentity();
637   glRotatef(current_device_rotation(), 0, 0, 1);
638
639   /** setup perspectiv */
640   glTranslatef(0.0, 0.0, -1.5*BOARDSIZE);
641   glRotatef(30.0, 1.0, 0.0, 0.0);
642   glRotatef(-current_device_rotation(), 0, 0, 1);
643   gltrackball_rotate (cs->trackball);
644   glRotatef(current_device_rotation(), 0, 0, 1);
645
646   glRotatef(cs->theta*100, 0.0, 1.0, 0.0);
647   glTranslatef(-0.5*BOARDSIZE, 0.0, -0.5*BOARDSIZE);
648
649 /*   cs->position[0] = 4.0 + 1.0*-sin(cs->theta*100*M_PI/180.0); */
650 /*   cs->position[2] = 4.0 + 1.0* cos(cs->theta*100*M_PI/180.0); */
651 /*   cs->position[1] = 5.0; */
652
653   /* this is the lone light that the shadow matrix is generated from */
654   cs->position[0] = 1.0;
655   cs->position[2] = 1.0;
656   cs->position[1] = 16.0;
657
658   cs->position2[0] = 4.0 + 8.0*-sin(cs->theta*100*M_PI/180.0);
659   cs->position2[2] = 4.0 + 8.0* cos(cs->theta*100*M_PI/180.0);
660
661   if (!cs->wire) {
662     glEnable(GL_LIGHTING);
663     glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
664     glLightfv(GL_LIGHT1, GL_POSITION, cs->position2);
665     glEnable(GL_LIGHT0);
666   }
667
668   /** draw board, pieces */
669   if(!cs->wire) {
670     glEnable(GL_LIGHTING);
671     glEnable(GL_COLOR_MATERIAL);
672
673     if(reflections && !cs->wire) {
674       draw_reflections(mi, cs);
675       glEnable(GL_BLEND);
676     }
677
678     glEnable(GL_TEXTURE_2D);
679     glBindTexture(GL_TEXTURE_2D, cs->boardtexture);
680     drawBoard(mi, cs);
681     glDisable(GL_TEXTURE_2D);
682
683     if(shadows) {
684       /* render shadows */
685       GLfloat m[4][4];
686       shadowmatrix(m, cs->ground, cs->position);
687
688       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialShadow);
689       glEnable(GL_BLEND);
690       glDisable(GL_LIGHTING);
691       glDisable(GL_DEPTH_TEST);
692       
693       /* display ant shadow */
694       glPushMatrix();
695       glTranslatef(0.0, 0.001, 0.0);
696       glMultMatrixf(m[0]);
697       glTranslatef(0.5, 0.01, 0.5);
698       draw_shadow_pieces(mi, cs, cs->wire);
699       glPopMatrix();      
700
701       glEnable(GL_LIGHTING);
702       glDisable(GL_BLEND);
703       glEnable(GL_DEPTH_TEST);
704     }
705
706     if(reflections)
707       glDisable(GL_BLEND);
708   }
709   else
710     drawBoard(mi, cs);
711  
712   glTranslatef(0.5, 0.0, 0.5);
713   draw_pieces(mi, cs, cs->wire);
714
715   if(!cs->wire) {
716     glDisable(GL_COLOR_MATERIAL);
717     glDisable(GL_LIGHTING);
718   }
719
720   if (!cs->button_down_p)
721     cs->theta += .002;
722 }
723
724 /** reshape handler */
725 ENTRYPOINT void reshape_chess(ModeInfo *mi, int width, int height) 
726 {
727   GLfloat h = (GLfloat) height / (GLfloat) width;
728   glViewport(0,0, width, height);
729   glMatrixMode(GL_PROJECTION);
730   glLoadIdentity();
731   gluPerspective(45, 1/h, 2.0, 30.0);
732   glMatrixMode(GL_MODELVIEW);
733 }
734
735 /** initialization handler */
736 ENTRYPOINT void init_chess(ModeInfo *mi) 
737 {
738   Chesscreen *cs;
739   int screen = MI_SCREEN(mi);
740
741   if(!qs && 
742      !(qs = (Chesscreen *) calloc(MI_NUM_SCREENS(mi), sizeof(Chesscreen))))
743     return;
744   
745   cs = &qs[screen];
746   cs->window = MI_WINDOW(mi);
747   cs->wire = MI_IS_WIREFRAME(mi);
748   cs->trackball = gltrackball_init ();
749   
750   cs->oldwhite = -1;
751
752   cs->colors[0][0] = 1.0;
753   cs->colors[0][1] = 0.5;
754   cs->colors[0][2] = 0.0;
755
756   cs->colors[1][0] = 0.6;
757   cs->colors[1][1] = 0.6;
758   cs->colors[1][2] = 0.6;
759
760   cs->done = 1;
761   cs->count = 99;
762   cs->mod = 1.4;
763
764 /*   cs->position[0] = 0.0; */
765 /*   cs->position[1] = 5.0; */
766 /*   cs->position[2] = 5.0; */
767 /*   cs->position[3] = 1.0; */
768
769   cs->position[0] = 0.0;
770   cs->position[1] = 24.0;
771   cs->position[2] = 2.0;
772   cs->position[3] = 1.0;
773
774
775   cs->position2[0] = 5.0;
776   cs->position2[1] = 5.0;
777   cs->position2[2] = 5.0;
778   cs->position2[3] = 1.0;
779
780   cs->ground[0] = 0.0;
781   cs->ground[1] = 1.0;
782   cs->ground[2] = 0.0;
783   cs->ground[3] = -0.00001;
784
785   cs->oldgame = -1;
786
787
788   if((cs->glx_context = init_GL(mi)))
789     reshape_chess(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
790   else
791     MI_CLEARWINDOW(mi);
792
793   if (!cs->wire) {
794     glDepthFunc(GL_LEQUAL);
795     glClearStencil(0);
796     glEnable(GL_CULL_FACE);
797     glCullFace(GL_BACK);
798
799     make_piece_texture(cs);
800     make_board_texture(cs);
801   }
802   gen_model_lists( classic, cs->poly_counts);
803
804   if(!cs->wire) {
805     setup_lights(cs);
806     glColorMaterial(GL_FRONT, GL_DIFFUSE);
807     glShadeModel(smooth ? GL_SMOOTH : GL_FLAT);
808     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
809     glEnable(GL_DEPTH_TEST);
810   }
811   else
812     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
813 }
814
815 /** does dirty work drawing scene, moving pieces */
816 ENTRYPOINT void draw_chess(ModeInfo *mi) 
817 {
818   Chesscreen *cs = &qs[MI_SCREEN(mi)];
819   Window w = MI_WINDOW(mi);
820   Display *disp = MI_DISPLAY(mi);
821
822   if(!cs->glx_context)
823     return;
824
825   glXMakeCurrent(disp, w, *(cs->glx_context));
826
827   /** code for moving a piece */
828   if(cs->moving && ++cs->steps == 100) {
829     cs->moving = cs->count = cs->steps = cs->take = 0;
830     cs->game.board[cs->game.moves[cs->mc][2]][cs->game.moves[cs->mc][3]] = cs->mpiece;
831     ++cs->mc;
832     
833     if(cs->mc == cs->game.movecount) {
834       cs->done = 1;
835       cs->mc = 0;
836     }
837   }
838
839   if(++cs->count == 100) {
840     if(!cs->done) {
841       cs->mpiece = cs->game.board[cs->game.moves[cs->mc][0]][cs->game.moves[cs->mc][1]];
842       cs->game.board[cs->game.moves[cs->mc][0]][cs->game.moves[cs->mc][1]] = NONE;
843       
844       if((cs->tpiece = cs->game.board[cs->game.moves[cs->mc][2]][cs->game.moves[cs->mc][3]])) {
845         cs->game.board[cs->game.moves[cs->mc][2]][cs->game.moves[cs->mc][3]] = NONE;
846         cs->take = 1;
847       }
848       
849       cs->from[0] = cs->game.moves[cs->mc][0];
850       cs->from[1] = cs->game.moves[cs->mc][1];
851       cs->to[0] = cs->game.moves[cs->mc][2];
852       cs->to[1] = cs->game.moves[cs->mc][3];
853       
854       cs->dz = (cs->to[0] - cs->from[0]) / 100;
855       cs->dx = (cs->to[1] - cs->from[1]) / 100;
856       cs->steps = 0;
857       cs->moving = 1;
858     }
859     else if(cs->done == 1) {
860       int newgame = cs->oldgame;
861       while(newgame == cs->oldgame)
862         newgame = random()%GAMES;
863
864       /* mod the mod */
865       cs->mod = 0.6 + (random()%20)/10.0;
866
867       /* same old game */
868       cs->oldgame = newgame;
869       cs->game = games[cs->oldgame];
870       build_colors(cs);
871       cs->done = 2;
872       cs->count = 0;
873     }
874     else {
875       cs->done = 0;
876       cs->count = 0;
877     }
878   }
879
880   /* set lighting */
881   if(cs->done) {
882     glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 
883              cs->done == 1 ? 1.0+0.1*cs->count : 100.0/cs->count);
884     glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 
885              cs->done == 1 ? 1.0+0.1*cs->count : 100.0/cs->count);
886     glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.14);
887     glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.14);
888   }
889
890   display(mi, cs);
891
892   if(mi->fps_p) do_fps(mi);
893   glFinish(); 
894   glXSwapBuffers(disp, w);
895 }
896
897 /** bust it */
898 ENTRYPOINT void release_chess(ModeInfo *mi) 
899 {
900   if(qs)
901     free((void *) qs);
902   qs = 0;
903
904   FreeAllGL(mi);
905 }
906
907 XSCREENSAVER_MODULE_2 ("Endgame", endgame, chess)
908
909 #endif