http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.05.tar.gz
[xscreensaver] / hacks / glx / endgame.c
index ecc58b52740eeca5c722cd5b0f128e942dd57e3d..857183bfb69f5b4ce56abae7eb8a87c5e69d3c41 100644 (file)
@@ -4,7 +4,7 @@
  *
  * version 1.0 - June 6, 2002
  *
- * Copyright (C) 2002 Blair Tennessy (tennessb@unbc.ca)
+ * Copyright (C) 2002-2008 Blair Tennessy (tennessb@unbc.ca)
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  * implied warranty.
  */
 
-#include <X11/Intrinsic.h>
-
 #ifdef STANDALONE
-# define PROGCLASS        "Endgame"
-# define HACK_INIT        init_chess
-# define HACK_DRAW        draw_chess
-# define HACK_RESHAPE     reshape_chess
-# define HACK_HANDLE_EVENT chess_handle_event
-# define EVENT_MASK       PointerMotionMask
-# define chess_opts  xlockmore_opts
-
-#define DEFAULTS       "*delay:       20000       \n" \
-                       "*showFPS:       False       \n" \
-                      "*wireframe:     False     \n"   \
+#define DEFAULTS       "*delay:     20000 \n" \
+                       "*showFPS:   False \n" \
+                      "*wireframe: False \n" \
 
+# define refresh_chess 0
 # include "xlockmore.h"
 
 #else
 
 #ifdef USE_GL
 
-#include <GL/glu.h>
+#define BOARDSIZE 8
+#define WHITES 5
+
 #include "gltrackball.h"
 #include "chessmodels.h"
+#include "chessgames.h"
 
 #undef countof
 #define countof(x) (sizeof((x))/sizeof((*x)))
 
 static XrmOptionDescRec opts[] = {
-  {"+rotate", ".chess.rotate", XrmoptionNoArg, (caddr_t) "false" },
-  {"-rotate", ".chess.rotate", XrmoptionNoArg, (caddr_t) "true" },
+  {"+rotate", ".chess.rotate", XrmoptionNoArg, "false" },
+  {"-rotate", ".chess.rotate", XrmoptionNoArg, "true" },
+  {"+reflections", ".chess.reflections", XrmoptionNoArg, "false" },
+  {"-reflections", ".chess.reflections", XrmoptionNoArg, "true" },
+  {"+shadows", ".chess.shadows", XrmoptionNoArg, "false" },
+  {"-shadows", ".chess.shadows", XrmoptionNoArg, "true" },
+  {"+smooth", ".chess.smooth", XrmoptionNoArg, "false" },
+  {"-smooth", ".chess.smooth", XrmoptionNoArg, "true" },
+  {"+classic", ".chess.classic", XrmoptionNoArg, "false" },
+  {"-classic", ".chess.classic", XrmoptionNoArg, "true" },
 };
 
-int rotate;
+static int rotate, reflections, smooth, shadows, classic;
 
 static argtype vars[] = {
-  {(caddr_t *) &rotate, "rotate", "Rotate", "True", t_Bool},
+  {&rotate,      "rotate",      "Rotate",      "True", t_Bool},
+  {&reflections, "reflections", "Reflections", "True", t_Bool},
+  {&shadows, "shadows", "Shadows", "True", t_Bool},
+  {&smooth,      "smooth",      "Smooth",      "True", t_Bool},
+  {&classic,     "classic",     "Classic",     "False", t_Bool},
 };
 
-ModeSpecOpt chess_opts = {countof(opts), opts, countof(vars), vars, NULL};
+ENTRYPOINT ModeSpecOpt chess_opts = {countof(opts), opts, countof(vars), vars, NULL};
 
 #ifdef USE_MODULES
 ModStruct   chess_description =
@@ -67,122 +73,154 @@ ModStruct   chess_description =
 
 #endif
 
+#define checkImageWidth 16
+#define checkImageHeight 16
+
 typedef struct {
   GLXContext *glx_context;
   Window window;
   trackball_state *trackball;
   Bool button_down_p;
-} Chesscreen;
 
-static Chesscreen *qs = NULL;
+  ChessGame game;
+  int oldwhite;
 
-#include <math.h>
-#include <sys/time.h>
-#include <stdio.h>
-#include <stdlib.h>
+  /** definition of white/black (orange/gray) colors */
+  GLfloat colors[2][3];
 
-#ifndef M_PI
-#define M_PI 3.14159265
-#endif
+  GLubyte checkImage[checkImageWidth][checkImageHeight][3];
+  GLuint piecetexture, boardtexture;
 
-#define BOARDSIZE 8
+  int mpiece, tpiece, steps, done;
+  double from[2], to[2];
+  double dx, dz;
+  int moving, take, mc, count, wire;
+  double theta;
 
-/** definition of white/black (orange/gray) colors */
-GLfloat colors[2][3] = 
-  { 
-    {1.0, 0.5, 0.0},
-    {0.5, 0.5, 0.5},
-  };
+  GLfloat position[4];
+  GLfloat position2[4];
+
+  GLfloat mod;
+
+  GLfloat ground[4];
+
+  int oldgame;
+
+  int poly_counts[PIECES];  /* polygon count of each type of piece */
+
+
+} Chesscreen;
+
+static Chesscreen *qs = NULL;
+
+static const GLfloat MaterialShadow[] =   {0.0, 0.0, 0.0, 0.3};
 
-GLfloat whites[3][3] = 
+
+/* i prefer silvertip */
+static const GLfloat whites[WHITES][3] = 
   {
-    {1.0, 0.5, 0.0},
-    {0.8, 0.45, 1.0},
-    {0.37, 0.56, 0.87},   
+    {1.0, 0.55, 0.1},
+    {0.8, 0.52, 0.8},
+    {0.43, 0.54, 0.76},
+    {0.2, 0.2, 0.2},
+    {0.35, 0.60, 0.35},
   };
 
-/* int board[BOARDSIZE][BOARDSIZE]; */
+static void build_colors(Chesscreen *cs) 
+{
 
-#include "chessgames.h"
+  /* find new white */
+  int newwhite = cs->oldwhite;
+  while(newwhite == cs->oldwhite)
+    newwhite = random()%WHITES;
+  cs->oldwhite = newwhite;
+
+  cs->colors[0][0] = whites[cs->oldwhite][0];
+  cs->colors[0][1] = whites[cs->oldwhite][1];
+  cs->colors[0][2] = whites[cs->oldwhite][2];
+}
+
+/* build piece texture */
+static void make_piece_texture(Chesscreen *cs) 
+{
+  int i, j, c;
+
+  for (i = 0; i < checkImageWidth; i++) {
+    for (j = 0; j < checkImageHeight; j++) {
+      c = ((j%2) == 0 || i%2 == 0) ? 240 : 180+random()%16;
+      cs->checkImage[i][j][0] = (GLubyte) c;
+      cs->checkImage[i][j][1] = (GLubyte) c;
+      cs->checkImage[i][j][2] = (GLubyte) c;
+    }
+  }
+
+  glGenTextures(1, &cs->piecetexture);
+  glBindTexture(GL_TEXTURE_2D, cs->piecetexture);
 
-ChessGame game;
-
-/* void buildBoard(void) { */
-/*   board[0][5] = BKING; */
-/*   board[1][4] = BPAWN; */
-/*   board[1][2] = BPAWN; */
-/*   board[1][0] = BPAWN; */
-/*   board[2][2] = BPAWN; */
-/*   board[2][4] = BPAWN; */
-/*   board[2][7] = KNIGHT; */
-/*   board[3][0] = PAWN; */
-/*   board[3][2] = ROOK; */
-/*   board[4][0] = PAWN; */
-/*   board[4][4] = KING; */
-/*   board[4][5] = PAWN; */
-/*   board[6][0] = BPAWN; */
-/*   board[6][7] = PAWN; */
-/*   board[7][0] = BBISHOP; */
-/* } */
-
-void build_colors(void) {
-  int white = random()%3;
-  colors[0][0] = whites[white][0];
-  colors[0][1] = whites[white][1];
-  colors[0][2] = whites[white][2];
+  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
+  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
+  glTexImage2D(GL_TEXTURE_2D, 0, 3, checkImageWidth, 
+              checkImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, 
+              &cs->checkImage[0][0]);
 }
 
-/* int moves[MOVES][4] =  */
-/*   { {3, 2, 6, 2}, */
-/*     {7, 0, 6, 1}, */
-/*     {6, 2, 6, 6}, */
-/*     {0, 5, 0, 4}, */
-/*     {6, 6, 0, 6}, */
-/*     {0, 4, 1, 3}, */
-/*     {2, 7, 1, 5}, */
-/*     {2, 2, 3, 2}, */
-/*     {0, 6, 0, 3},  */
-/*     {1, 3, 2, 2}, */
-/*     {0, 3, 6, 3}, */
-/*     {3, 2, 4, 2}, /\* pawn to bishop 5 *\/ */
-/*     {1, 5, 0, 3}, /\* check *\/ */
-/*     {2, 2, 3, 2}, */
-/*     {0, 3, 2, 4}, /\* takes pawn *\/ */
-/*     {3, 2, 2, 2}, */
-/*     {2, 4, 0, 3}, */
-/*     {2, 2, 3, 2}, */
-/*     {6, 3, 6, 1}, /\* rook takes bishop *\/ */
-/*     {6, 0, 7, 0}, */
-/*     {6, 1, 3, 1}, */
-/*     {3, 2, 2, 3}, */
-/*     {3, 1, 3, 3}, */
-/*     {0, 0, 2, 3}, */
-/*   }; */
-
-/* yay its c */
-int mpiece = 0, tpiece, steps = 0, done = 1;
-double from[2], to[2];
-double dx, dz;
-int moving = 0, take = 0, mc = 0, count = 99, wire = 0;
+/* build board texture (uniform noise in [180,180+50]) */
+static void make_board_texture(Chesscreen *cs) 
+{
+  int i, j, c;
+
+  for (i = 0; i < checkImageWidth; i++) {
+    for (j = 0; j < checkImageHeight; j++) {
+      c = 180 + random()%51;
+      cs->checkImage[i][j][0] = (GLubyte) c;
+      cs->checkImage[i][j][1] = (GLubyte) c;
+      cs->checkImage[i][j][2] = (GLubyte) c;
+    }
+  }
+
+  glGenTextures(1, &cs->boardtexture);
+  glBindTexture(GL_TEXTURE_2D, cs->boardtexture);
+
+  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
+  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
+  glTexImage2D(GL_TEXTURE_2D, 0, 3, checkImageWidth, 
+              checkImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, 
+              &cs->checkImage[0][0]);
+}
 
 /** handle X event (trackball) */
-Bool chess_handle_event (ModeInfo *mi, XEvent *event) {
-  Chesscreen *c = &qs[MI_SCREEN(mi)];
+ENTRYPOINT Bool chess_handle_event (ModeInfo *mi, XEvent *event) 
+{
+  Chesscreen *cs = &qs[MI_SCREEN(mi)];
 
-  if(event->xany.type == ButtonPress && event->xbutton.button & Button1) {
-    c->button_down_p = True;
-    gltrackball_start (c->trackball,
+  if(event->xany.type == ButtonPress && event->xbutton.button == Button1) {
+    cs->button_down_p = True;
+    gltrackball_start (cs->trackball,
                       event->xbutton.x, event->xbutton.y,
                       MI_WIDTH (mi), MI_HEIGHT (mi));
     return True;
   }
   else if(event->xany.type == ButtonRelease 
-         && event->xbutton.button & Button1) {
-    c->button_down_p = False;
+         && event->xbutton.button == Button1) {
+    cs->button_down_p = False;
     return True;
   }
-  else if(event->xany.type == MotionNotify && c->button_down_p) {
-    gltrackball_track (c->trackball,
+  else if (event->xany.type == ButtonPress &&
+           (event->xbutton.button == Button4 ||
+            event->xbutton.button == Button5 ||
+            event->xbutton.button == Button6 ||
+            event->xbutton.button == Button7))
+    {
+      gltrackball_mousewheel (cs->trackball, event->xbutton.button, 5,
+                              !event->xbutton.state);
+      return True;
+    }
+  else if(event->xany.type == MotionNotify && cs->button_down_p) {
+    gltrackball_track (cs->trackball,
                       event->xmotion.x, event->xmotion.y,
                       MI_WIDTH (mi), MI_HEIGHT (mi));
     return True;
@@ -191,25 +229,41 @@ Bool chess_handle_event (ModeInfo *mi, XEvent *event) {
   return False;
 }
 
-GLfloat position[] = { 3.0, 8.0, 3.0, 1.0 };
+static const GLfloat diffuse2[] = {1.0, 1.0, 1.0, 1.0};
+/*static const GLfloat ambient2[] = {0.7, 0.7, 0.7, 1.0};*/
+static const GLfloat shininess[] = {60.0};
+static const GLfloat specular[] = {0.4, 0.4, 0.4, 1.0};
 
 /* configure lighting */
-void setup_lights(void) {
+static void setup_lights(Chesscreen *cs) 
+{
   glEnable(GL_LIGHTING);
-  glLightfv(GL_LIGHT0, GL_POSITION, position);
+  glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
+  glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse2);
   glEnable(GL_LIGHT0);
+
+/*   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient2); */
+
+  glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
+  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
+
+  glLightfv(GL_LIGHT1, GL_SPECULAR, diffuse2);
+  glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse2);
+  glEnable(GL_LIGHT1);
 }
 
-/** draw pieces */
-void drawPieces(void) {
+/* draw pieces */
+static void drawPieces(ModeInfo *mi, Chesscreen *cs) 
+{
   int i, j;
 
   for(i = 0; i < BOARDSIZE; ++i) {
     for(j = 0; j < BOARDSIZE; ++j) {
-      if(game.board[i][j]) {   
-       int c = game.board[i][j]/PIECES;
-       glColor3fv(colors[c]);
-       glCallList(game.board[i][j]%PIECES);
+      if(cs->game.board[i][j]) {       
+       int c = cs->game.board[i][j]/PIECES;
+       glColor3fv(cs->colors[c]);
+       glCallList(cs->game.board[i][j]%PIECES);
+        mi->polygon_count += cs->poly_counts[cs->game.board[i][j]%PIECES];
       }
       
       glTranslatef(1.0, 0.0, 0.0);
@@ -221,82 +275,155 @@ void drawPieces(void) {
   glTranslatef(0.0, 0.0, -1.0*BOARDSIZE);
 }
 
-/** draw a moving piece */
-void drawMovingPiece(void) {
-  int piece = mpiece % PIECES;
+/* draw pieces */
+static void drawPiecesShadow(ModeInfo *mi, Chesscreen *cs) 
+{
+  int i, j;
+
+  for(i = 0; i < BOARDSIZE; ++i) {
+    for(j = 0; j < BOARDSIZE; ++j) {
+      if(cs->game.board[i][j]) {       
+       glColor4f(0.0, 0.0, 0.0, 0.4);
+       glCallList(cs->game.board[i][j]%PIECES);
+        mi->polygon_count += cs->poly_counts[cs->game.board[i][j]%PIECES];
+      }
+      
+      glTranslatef(1.0, 0.0, 0.0);
+    }
+    
+    glTranslatef(-1.0*BOARDSIZE, 0.0, 1.0);
+  }
+
+  glTranslatef(0.0, 0.0, -1.0*BOARDSIZE);
+}
+
+/* draw a moving piece */
+static void drawMovingPiece(ModeInfo *mi, Chesscreen *cs, int shadow) 
+{
+  int piece = cs->mpiece % PIECES;
 
   glPushMatrix();
-  glColor3fv(colors[mpiece/PIECES]);
+
+  if(shadow) glColor4fv(MaterialShadow);
+  else glColor3fv(cs->colors[cs->mpiece/PIECES]);
 
   /** assume a queening.  should be more general */
-  if((mpiece == PAWN  && fabs(to[0]) < 0.01) || 
-     (mpiece == BPAWN && fabs(to[0]-7.0) < 0.01)) {
-    glTranslatef(from[1]+steps*dx, 0.0, from[0]+steps*dz);
-    glColor4f(colors[mpiece/7][0], colors[mpiece/7][1], colors[mpiece/7][2],
-             (fabs(50.0-steps))/50.0);
-    piece = steps < 50 ? PAWN : QUEEN;
+  if((cs->mpiece == PAWN  && fabs(cs->to[0]) < 0.01) || 
+     (cs->mpiece == BPAWN && fabs(cs->to[0]-7.0) < 0.01)) {
+    glTranslatef(cs->from[1]+cs->steps*cs->dx, 0.0, cs->from[0]+cs->steps*cs->dz);
+
+    glColor4f(shadow ? MaterialShadow[0] : cs->colors[cs->mpiece/7][0], 
+             shadow ? MaterialShadow[1] : cs->colors[cs->mpiece/7][1], 
+             shadow ? MaterialShadow[2] : cs->colors[cs->mpiece/7][2],
+             (fabs(50.0-cs->steps))/50.0);
+
+    piece = cs->steps < 50 ? PAWN : QUEEN;
 
     /* what a kludge */
-    if(steps == 99)
-      mpiece = mpiece == PAWN ? QUEEN : BQUEEN;
+    if(cs->steps == 99)
+      cs->mpiece = cs->mpiece == PAWN ? QUEEN : BQUEEN;
   }
-  else if(mpiece % PIECES == KNIGHT) {
-    glTranslatef(steps < 50 ? from[1] : to[1], 0.0, 
-                steps < 50 ? from[0] : to[0]);
-
-    glColor4f(colors[mpiece/7][0], colors[mpiece/7][1], colors[mpiece/7][2],
-             fabs(49-steps)/49.0);
-    glScalef(fabs(49-steps)/49.0, fabs(49-steps)/49.0, fabs(49-steps)/49.0);
+  else if(cs->mpiece % PIECES == KNIGHT) {
+    GLfloat shine[1];
+    GLfloat spec[4];
+    GLfloat mult;
+    glTranslatef(cs->steps < 50 ? cs->from[1] : cs->to[1], 0.0, 
+                cs->steps < 50 ? cs->from[0] : cs->to[0]);
+
+    mult = cs->steps < 10 
+      ? (1.0 - cs->steps / 10.0) : 100 - cs->steps < 10 
+      ? (1.0 - (100 - cs->steps) / 10.0) : 0.0;
+
+    shine[0] = mult*shininess[0];
+    spec[0] = mult*specular[0];
+    spec[1] = mult*specular[1];
+    spec[2] = mult*specular[2];
+    spec[3] = 1.0;
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shine);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spec);
+
+    glColor4f(shadow ? MaterialShadow[0] : cs->colors[cs->mpiece/7][0], 
+             shadow ? MaterialShadow[1] : cs->colors[cs->mpiece/7][1], 
+             shadow ? MaterialShadow[2] : cs->colors[cs->mpiece/7][2],
+             fabs(49-cs->steps)/49.0);
+
+    glScalef(fabs(49-cs->steps)/49.0, fabs(49-cs->steps)/49.0, fabs(49-cs->steps)/49.0);
   }
   else
-    glTranslatef(from[1]+steps*dx, 0.0, from[0]+steps*dz);
+    glTranslatef(cs->from[1]+cs->steps*cs->dx, 0.0, cs->from[0]+cs->steps*cs->dz);
 
-  if(!wire)
+  if(!cs->wire)
     glEnable(GL_BLEND);
   
   glCallList(piece);
+  mi->polygon_count += cs->poly_counts[cs->mpiece % PIECES];
+
+  glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
+  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
+
   glPopMatrix();
 
-  if(!wire)
+  if(!cs->wire)
     glDisable(GL_BLEND);
 }
 
 /** code to squish a taken piece */
-void drawTakePiece(void) {
-  if(!wire)
+static void drawTakePiece(ModeInfo *mi, Chesscreen *cs, int shadow) 
+{
+  if(!cs->wire)
     glEnable(GL_BLEND);
 
-  glColor4f(colors[tpiece/7][0], colors[tpiece/7][1], colors[tpiece/7][2],
-            (100-1.6*steps)/100.0);
+  glColor4f(shadow ? MaterialShadow[0] : cs->colors[cs->tpiece/7][0], 
+           shadow ? MaterialShadow[1] : cs->colors[cs->tpiece/7][1], 
+           shadow ? MaterialShadow[2] : cs->colors[cs->tpiece/7][2],
+            (100-1.6*cs->steps)/100.0);
 
-  glTranslatef(to[1], 0.0, to[0]);
+  glTranslatef(cs->to[1], 0.0, cs->to[0]);
   
-  if(mpiece % PIECES == KNIGHT)
-    glScalef(1.0+steps/100.0, 1.0, 1.0+steps/100.0);
+  if(cs->mpiece % PIECES == KNIGHT)
+    glScalef(1.0+cs->steps/100.0, 1.0, 1.0+cs->steps/100.0);
   else
-    glScalef(1.0, 1 - steps/50.0 > 0.01 ? 1 - steps/50.0 : 0.01, 1.0);
-  glCallList(tpiece % 7);
+    glScalef(1.0, 1 - cs->steps/50.0 > 0.01 ? 1 - cs->steps/50.0 : 0.01, 1.0);
+  glCallList(cs->tpiece % 7);
+  mi->polygon_count += cs->poly_counts[cs->tpiece % PIECES];
   
-  if(!wire)
+  if(!cs->wire)
     glDisable(GL_BLEND);
 }
 
 /** draw board */
-void drawBoard(void) {
+static void drawBoard(ModeInfo *mi, Chesscreen *cs) 
+{
   int i, j;
 
   glBegin(GL_QUADS);
 
   for(i = 0; i < BOARDSIZE; ++i)
     for(j = 0; j < BOARDSIZE; ++j) {
+      double ma1 = (i+j)%2 == 0 ? cs->mod*i : 0.0;
+      double mb1 = (i+j)%2 == 0 ? cs->mod*j : 0.0;
+      double ma2 = (i+j)%2 == 0 ? cs->mod*(i+1.0) : 0.01;
+      double mb2 = (i+j)%2 == 0 ? cs->mod*(j+1.0) : 0.01;
+
       /*glColor3fv(colors[(i+j)%2]);*/
-      glColor4f(colors[(i+j)%2][0], colors[(i+j)%2][1],
-               colors[(i+j)%2][2], 0.8);
+      glColor4f(cs->colors[(i+j)%2][0], cs->colors[(i+j)%2][1],
+               cs->colors[(i+j)%2][2], 0.65);
+      
       glNormal3f(0.0, 1.0, 0.0);
+/*       glTexCoord2f(mod*i, mod*(j+1.0)); */
+      glTexCoord2f(ma1, mb2);
       glVertex3f(i, 0.0, j + 1.0);
+/*       glTexCoord2f(mod*(i+1.0), mod*(j+1.0)); */
+      glTexCoord2f(ma2, mb2);
       glVertex3f(i + 1.0, 0.0, j + 1.0);
+      glTexCoord2f(ma2, mb1);
+/*       glTexCoord2f(mod*(i+1.0), mod*j); */
       glVertex3f(i + 1.0, 0.0, j);
+      glTexCoord2f(ma1, mb1);
+/*       glTexCoord2f(mod*i, mod*j); */
       glVertex3f(i, 0.0, j);
+
+      mi->polygon_count++;
     }
 
   /* chop underneath board */
@@ -309,16 +436,144 @@ void drawBoard(void) {
   glEnd();
 }
 
-double theta = 0.0;
+static void draw_pieces(ModeInfo *mi, Chesscreen *cs, int wire) 
+{
+  if (!cs->wire) {
+    glEnable(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, cs->piecetexture);
+    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+    glColor4f(0.5, 0.5, 0.5, 1.0);
+    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialShadow);
+  }
 
-void draw_pieces(void) {
-  drawPieces();
-  if(moving) drawMovingPiece();
-  if(take) drawTakePiece();
+  drawPieces(mi, cs);
+  if(cs->moving) drawMovingPiece(mi, cs, 0);
+  if(cs->take) drawTakePiece(mi, cs, 0);
+  glDisable(GL_TEXTURE_2D);
+}
+
+static void draw_shadow_pieces(ModeInfo *mi, Chesscreen *cs, int wire) 
+{
+  if (!cs->wire) {
+    glEnable(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, cs->piecetexture);
+    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+  }
+
+  /* use the stencil */
+  glDisable(GL_LIGHTING);
+  glDisable(GL_COLOR_MATERIAL);
+  glDisable(GL_DEPTH_TEST);
+  glDisable(GL_TEXTURE_2D);
+  glDisable(GL_BLEND);
+
+  glClear(GL_STENCIL_BUFFER_BIT);
+  glColorMask(0,0,0,0);
+  glEnable(GL_STENCIL_TEST);
+
+  glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFFL);
+  glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
+  
+
+  glPushMatrix();
+  glTranslatef(0.0, 0.001, 0.0);
+
+  /* draw the pieces */
+  drawPiecesShadow(mi, cs);
+  if(cs->moving) drawMovingPiece(mi, cs, shadows);
+  if(cs->take) drawTakePiece(mi, cs, shadows);
+
+  glPopMatrix();
+
+
+  /* turn on drawing into colour buffer */
+  glColorMask(1,1,1,1);
+
+  /* programming with effect */
+  glDisable(GL_LIGHTING);
+  glDisable(GL_COLOR_MATERIAL);
+  glDisable(GL_TEXTURE_2D);
+
+  /* now draw the union of the shadows */
+
+  /* 
+     <todo>
+     want to keep alpha values (alpha is involved in transition
+     effects of the active pieces).
+     </todo>
+  */
+  glStencilFunc(GL_NOTEQUAL, 0, 0xFFFFFFFFL);
+  glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+
+  glEnable(GL_BLEND);
+
+  glColor4fv(MaterialShadow);
+
+  /* draw the board generously to fill the shadows */
+  glBegin(GL_QUADS);
+
+  glVertex3f(-1.0, 0.0, -1.0);
+  glVertex3f(-1.0, 0.0, BOARDSIZE + 1.0);
+  glVertex3f(1.0 + BOARDSIZE, 0.0, BOARDSIZE + 1.0);
+  glVertex3f(1.0 + BOARDSIZE, 0.0, -1.0);
+
+  glEnd();
+
+  glDisable(GL_STENCIL_TEST);
+
+  /* "pop" attributes */
+  glEnable(GL_TEXTURE_2D);
+  glEnable(GL_COLOR_MATERIAL);
+  glEnable(GL_LIGHTING);
+  glEnable(GL_CULL_FACE);
+
+
+
+}
+
+enum {X, Y, Z, W};
+enum {A, B, C, D};
+
+/* create a matrix that will project the desired shadow */
+static void shadowmatrix(GLfloat shadowMat[4][4],
+                 GLfloat groundplane[4],
+                 GLfloat lightpos[4]) 
+{
+  GLfloat dot;
+
+  /* find dot product between light position vector and ground plane normal */
+  dot = groundplane[X] * lightpos[X] +
+        groundplane[Y] * lightpos[Y] +
+        groundplane[Z] * lightpos[Z] +
+        groundplane[W] * lightpos[W];
+
+  shadowMat[0][0] = dot - lightpos[X] * groundplane[X];
+  shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y];
+  shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z];
+  shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W];
+
+  shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X];
+  shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y];
+  shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z];
+  shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W];
+
+  shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X];
+  shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y];
+  shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z];
+  shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W];
+
+  shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X];
+  shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y];
+  shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z];
+  shadowMat[3][3] = dot - lightpos[W] * groundplane[W];
 }
 
 /** reflectionboard */
-void draw_reflections(void) {
+static void draw_reflections(ModeInfo *mi, Chesscreen *cs) 
+{
+  int i, j;
+
   glEnable(GL_STENCIL_TEST);
   glStencilFunc(GL_ALWAYS, 1, 1);
   glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
@@ -327,10 +582,17 @@ void draw_reflections(void) {
 
   glDisable(GL_DEPTH_TEST);
   glBegin(GL_QUADS);
-  glVertex3f(0,         0,  BOARDSIZE);
-  glVertex3f(0,         0,  0);
-  glVertex3f(BOARDSIZE, 0,  0);
-  glVertex3f(BOARDSIZE, 0,  BOARDSIZE);
+
+  /* only draw white squares */
+  for(i = 0; i < BOARDSIZE; ++i) {
+    for(j = (BOARDSIZE+i) % 2; j < BOARDSIZE; j += 2) {
+      glVertex3f(i, 0.0, j + 1.0);
+      glVertex3f(i + 1.0, 0.0, j + 1.0);
+      glVertex3f(i + 1.0, 0.0, j);
+      glVertex3f(i, 0.0, j);
+      mi->polygon_count++;
+    }
+  }
   glEnd();
   glEnable(GL_DEPTH_TEST);
 
@@ -341,13 +603,13 @@ void draw_reflections(void) {
   glPushMatrix(); 
   glScalef(1.0, -1.0, 1.0);
   glTranslatef(0.5, 0.0, 0.5);
-  
-  glLightfv(GL_LIGHT0, GL_POSITION, position);
-  draw_pieces();
+
+  glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
+  draw_pieces(mi, cs, cs->wire);
   glPopMatrix();
   
   glDisable(GL_STENCIL_TEST);
-  glLightfv(GL_LIGHT0, GL_POSITION, position);
+  glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
 
   glEnable(GL_CULL_FACE);
   glCullFace(GL_BACK);
@@ -355,45 +617,100 @@ void draw_reflections(void) {
 }
 
 /** draws the scene */
-void display(Chesscreen *c) {
+static void display(ModeInfo *mi, Chesscreen *cs) 
+{
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 
+  mi->polygon_count = 0;
+
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
 
-  /** setup perspectif */
+  /** setup perspectiv */
   glTranslatef(0.0, 0.0, -1.5*BOARDSIZE);
   glRotatef(30.0, 1.0, 0.0, 0.0);
-  gltrackball_rotate (c->trackball);
-  glRotatef(theta*100, 0.0, 1.0, 0.0);
+  gltrackball_rotate (cs->trackball);
+  glRotatef(cs->theta*100, 0.0, 1.0, 0.0);
   glTranslatef(-0.5*BOARDSIZE, 0.0, -0.5*BOARDSIZE);
 
+/*   cs->position[0] = 4.0 + 1.0*-sin(cs->theta*100*M_PI/180.0); */
+/*   cs->position[2] = 4.0 + 1.0* cos(cs->theta*100*M_PI/180.0); */
+/*   cs->position[1] = 5.0; */
+
+  /* this is the lone light that the shadow matrix is generated from */
+  cs->position[0] = 1.0;
+  cs->position[2] = 1.0;
+  cs->position[1] = 16.0;
+
+  cs->position2[0] = 4.0 + 8.0*-sin(cs->theta*100*M_PI/180.0);
+  cs->position2[2] = 4.0 + 8.0* cos(cs->theta*100*M_PI/180.0);
+
+  if (!cs->wire) {
+    glEnable(GL_LIGHTING);
+    glLightfv(GL_LIGHT0, GL_POSITION, cs->position);
+    glLightfv(GL_LIGHT1, GL_POSITION, cs->position2);
+    glEnable(GL_LIGHT0);
+  }
+
   /** draw board, pieces */
-  if(!wire) {
+  if(!cs->wire) {
     glEnable(GL_LIGHTING);
     glEnable(GL_COLOR_MATERIAL);
-    draw_reflections();
-    glEnable(GL_BLEND);
-    drawBoard();
-    glDisable(GL_BLEND);
+
+    if(reflections && !cs->wire) {
+      draw_reflections(mi, cs);
+      glEnable(GL_BLEND);
+    }
+
+    glEnable(GL_TEXTURE_2D);
+    glBindTexture(GL_TEXTURE_2D, cs->boardtexture);
+    drawBoard(mi, cs);
+    glDisable(GL_TEXTURE_2D);
+
+    if(shadows) {
+      /* render shadows */
+      GLfloat m[4][4];
+      shadowmatrix(m, cs->ground, cs->position);
+
+      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialShadow);
+      glEnable(GL_BLEND);
+      glDisable(GL_LIGHTING);
+      glDisable(GL_DEPTH_TEST);
+      
+      /* display ant shadow */
+      glPushMatrix();
+      glTranslatef(0.0, 0.001, 0.0);
+      glMultMatrixf(m[0]);
+      glTranslatef(0.5, 0.01, 0.5);
+      draw_shadow_pieces(mi, cs, cs->wire);
+      glPopMatrix();      
+
+      glEnable(GL_LIGHTING);
+      glDisable(GL_BLEND);
+      glEnable(GL_DEPTH_TEST);
+    }
+
+    if(reflections)
+      glDisable(GL_BLEND);
   }
   else
-    drawBoard();
+    drawBoard(mi, cs);
  
-  glTranslatef(0.5, .01, 0.5);
-  draw_pieces();
+  glTranslatef(0.5, 0.0, 0.5);
+  draw_pieces(mi, cs, cs->wire);
 
-  if(!wire) {
+  if(!cs->wire) {
     glDisable(GL_COLOR_MATERIAL);
     glDisable(GL_LIGHTING);
   }
 
-  if (!c->button_down_p)
-    theta += .002;
+  if (!cs->button_down_p)
+    cs->theta += .002;
 }
 
 /** reshape handler */
-void reshape_chess(ModeInfo *mi, int width, int height) {
+ENTRYPOINT void reshape_chess(ModeInfo *mi, int width, int height) 
+{
   GLfloat h = (GLfloat) height / (GLfloat) width;
   glViewport(0,0, width, height);
   glMatrixMode(GL_PROJECTION);
@@ -403,107 +720,163 @@ void reshape_chess(ModeInfo *mi, int width, int height) {
 }
 
 /** initialization handler */
-void init_chess(ModeInfo *mi) {
-  Chesscreen *c;
+ENTRYPOINT void init_chess(ModeInfo *mi) 
+{
+  Chesscreen *cs;
   int screen = MI_SCREEN(mi);
-  wire = MI_IS_WIREFRAME(mi);
 
   if(!qs && 
      !(qs = (Chesscreen *) calloc(MI_NUM_SCREENS(mi), sizeof(Chesscreen))))
     return;
   
-  c = &qs[screen];
-  c->window = MI_WINDOW(mi);
-  c->trackball = gltrackball_init ();
+  cs = &qs[screen];
+  cs->window = MI_WINDOW(mi);
+  cs->wire = MI_IS_WIREFRAME(mi);
+  cs->trackball = gltrackball_init ();
   
-  if((c->glx_context = init_GL(mi)))
+  cs->oldwhite = -1;
+
+  cs->colors[0][0] = 1.0;
+  cs->colors[0][1] = 0.5;
+  cs->colors[0][2] = 0.0;
+
+  cs->colors[1][0] = 0.6;
+  cs->colors[1][1] = 0.6;
+  cs->colors[1][2] = 0.6;
+
+  cs->done = 1;
+  cs->count = 99;
+  cs->mod = 1.4;
+
+/*   cs->position[0] = 0.0; */
+/*   cs->position[1] = 5.0; */
+/*   cs->position[2] = 5.0; */
+/*   cs->position[3] = 1.0; */
+
+  cs->position[0] = 0.0;
+  cs->position[1] = 24.0;
+  cs->position[2] = 2.0;
+  cs->position[3] = 1.0;
+
+
+  cs->position2[0] = 5.0;
+  cs->position2[1] = 5.0;
+  cs->position2[2] = 5.0;
+  cs->position2[3] = 1.0;
+
+  cs->ground[0] = 0.0;
+  cs->ground[1] = 1.0;
+  cs->ground[2] = 0.0;
+  cs->ground[3] = -0.00001;
+
+  cs->oldgame = -1;
+
+
+  if((cs->glx_context = init_GL(mi)))
     reshape_chess(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
   else
     MI_CLEARWINDOW(mi);
 
   glClearColor(0.0, 0.0, 0.0, 0.0);
 
-  glDepthFunc(GL_LEQUAL);
-  glClearStencil(0);
-  glEnable(GL_CULL_FACE);
-  glCullFace(GL_BACK);
+  if (!cs->wire) {
+    glDepthFunc(GL_LEQUAL);
+    glClearStencil(0);
+    glEnable(GL_CULL_FACE);
+    glCullFace(GL_BACK);
 
-  gen_model_lists();
+    make_piece_texture(cs);
+    make_board_texture(cs);
+  }
+  gen_model_lists( classic, cs->poly_counts);
 
-  if (!wire) {
-    setup_lights();
+  if(!cs->wire) {
+    setup_lights(cs);
     glColorMaterial(GL_FRONT, GL_DIFFUSE);
-    glShadeModel(GL_SMOOTH);
+    glShadeModel(smooth ? GL_SMOOTH : GL_FLAT);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     glEnable(GL_DEPTH_TEST);
   }
   else
-    glPolygonMode(GL_FRONT, GL_LINE);
-
-/*   buildBoard(); */
+    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 }
 
 /** does dirty work drawing scene, moving pieces */
-void draw_chess(ModeInfo *mi) {
-  Chesscreen *c = &qs[MI_SCREEN(mi)];
+ENTRYPOINT void draw_chess(ModeInfo *mi) 
+{
+  Chesscreen *cs = &qs[MI_SCREEN(mi)];
   Window w = MI_WINDOW(mi);
   Display *disp = MI_DISPLAY(mi);
 
-  if(!c->glx_context)
+  if(!cs->glx_context)
     return;
 
-  glXMakeCurrent(disp, w, *(c->glx_context));
+  glXMakeCurrent(disp, w, *(cs->glx_context));
 
   /** code for moving a piece */
-  if(moving && ++steps == 100) {
-    moving = count = steps = take = 0;
-    game.board[game.moves[mc][2]][game.moves[mc][3]] = mpiece;
-    ++mc;
+  if(cs->moving && ++cs->steps == 100) {
+    cs->moving = cs->count = cs->steps = cs->take = 0;
+    cs->game.board[cs->game.moves[cs->mc][2]][cs->game.moves[cs->mc][3]] = cs->mpiece;
+    ++cs->mc;
     
-    if(mc == game.movecount) {
-      done = 1;
-      mc = 0;
+    if(cs->mc == cs->game.movecount) {
+      cs->done = 1;
+      cs->mc = 0;
     }
   }
 
-  if(done)
-    glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 
-            done == 1 ? 1.0+0.1*count : 99.0/count);
-
-  if(++count == 100) {
-    if(!done) {
-      mpiece = game.board[game.moves[mc][0]][game.moves[mc][1]];
-      game.board[game.moves[mc][0]][game.moves[mc][1]] = NONE;
+  if(++cs->count == 100) {
+    if(!cs->done) {
+      cs->mpiece = cs->game.board[cs->game.moves[cs->mc][0]][cs->game.moves[cs->mc][1]];
+      cs->game.board[cs->game.moves[cs->mc][0]][cs->game.moves[cs->mc][1]] = NONE;
       
-      if((tpiece = game.board[game.moves[mc][2]][game.moves[mc][3]])) {
-       game.board[game.moves[mc][2]][game.moves[mc][3]] = NONE;
-       take = 1;
+      if((cs->tpiece = cs->game.board[cs->game.moves[cs->mc][2]][cs->game.moves[cs->mc][3]])) {
+       cs->game.board[cs->game.moves[cs->mc][2]][cs->game.moves[cs->mc][3]] = NONE;
+       cs->take = 1;
       }
       
-      from[0] = game.moves[mc][0];
-      from[1] = game.moves[mc][1];
-      to[0] = game.moves[mc][2];
-      to[1] = game.moves[mc][3];
+      cs->from[0] = cs->game.moves[cs->mc][0];
+      cs->from[1] = cs->game.moves[cs->mc][1];
+      cs->to[0] = cs->game.moves[cs->mc][2];
+      cs->to[1] = cs->game.moves[cs->mc][3];
       
-      dz = (to[0] - from[0]) / 100;
-      dx = (to[1] - from[1]) / 100;
-      steps = 0;
-      moving = 1;
+      cs->dz = (cs->to[0] - cs->from[0]) / 100;
+      cs->dx = (cs->to[1] - cs->from[1]) / 100;
+      cs->steps = 0;
+      cs->moving = 1;
     }
-    else if(done == 1) {
-      /* copy over new game */
-      game = games[random()%GAMES];
-      build_colors();
-      done = 2;
-      count = 0;
+    else if(cs->done == 1) {
+      int newgame = cs->oldgame;
+      while(newgame == cs->oldgame)
+       newgame = random()%GAMES;
+
+      /* mod the mod */
+      cs->mod = 0.6 + (random()%20)/10.0;
+
+      /* same old game */
+      cs->oldgame = newgame;
+      cs->game = games[cs->oldgame];
+      build_colors(cs);
+      cs->done = 2;
+      cs->count = 0;
     }
     else {
-      done = 0;
-      count = 0;
+      cs->done = 0;
+      cs->count = 0;
     }
   }
 
-  display(c);
+  /* set lighting */
+  if(cs->done) {
+    glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 
+            cs->done == 1 ? 1.0+0.1*cs->count : 100.0/cs->count);
+    glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 
+            cs->done == 1 ? 1.0+0.1*cs->count : 100.0/cs->count);
+    glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.14);
+    glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.14);
+  }
+
+  display(mi, cs);
 
   if(mi->fps_p) do_fps(mi);
   glFinish(); 
@@ -511,11 +884,15 @@ void draw_chess(ModeInfo *mi) {
 }
 
 /** bust it */
-void release_chess(ModeInfo *mi) {
+ENTRYPOINT void release_chess(ModeInfo *mi) 
+{
   if(qs)
     free((void *) qs);
+  qs = 0;
 
-  FreeAllGL(MI);
+  FreeAllGL(mi);
 }
 
+XSCREENSAVER_MODULE_2 ("Endgame", endgame, chess)
+
 #endif