3 * plays through a chess game ending. enjoy.
5 * version 1.0 - June 6, 2002
7 * Copyright (C) 2002 Blair Tennessy (tennessb@unbc.ca)
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
18 #include <X11/Intrinsic.h>
21 # define PROGCLASS "Endgame"
22 # define HACK_INIT init_chess
23 # define HACK_DRAW draw_chess
24 # define HACK_RESHAPE reshape_chess
25 # define HACK_HANDLE_EVENT chess_handle_event
26 # define EVENT_MASK PointerMotionMask
27 # define chess_opts xlockmore_opts
29 #define DEFAULTS "*delay: 20000 \n" \
30 "*showFPS: False \n" \
31 "*wireframe: False \n" \
33 # include "xlockmore.h"
42 #include "gltrackball.h"
43 #include "chessmodels.h"
46 #define countof(x) (sizeof((x))/sizeof((*x)))
48 static XrmOptionDescRec opts[] = {
49 {"+rotate", ".chess.rotate", XrmoptionNoArg, "false" },
50 {"-rotate", ".chess.rotate", XrmoptionNoArg, "true" },
51 {"+reflections", ".chess.reflections", XrmoptionNoArg, "false" },
52 {"-reflections", ".chess.reflections", XrmoptionNoArg, "true" },
53 {"+shadows", ".chess.shadows", XrmoptionNoArg, "false" },
54 {"-shadows", ".chess.shadows", XrmoptionNoArg, "true" },
55 {"+smooth", ".chess.smooth", XrmoptionNoArg, "false" },
56 {"-smooth", ".chess.smooth", XrmoptionNoArg, "true" },
59 int rotate, reflections, smooth, shadows;
61 static argtype vars[] = {
62 {&rotate, "rotate", "Rotate", "True", t_Bool},
63 {&reflections, "reflections", "Reflections", "True", t_Bool},
64 {&shadows, "shadows", "Shadows", "True", t_Bool},
65 {&smooth, "smooth", "Smooth", "True", t_Bool},
68 ModeSpecOpt chess_opts = {countof(opts), opts, countof(vars), vars, NULL};
71 ModStruct chess_description =
72 {"chess", "init_chess", "draw_chess", "release_chess",
73 "draw_chess", "init_chess", NULL, &chess_opts,
74 1000, 1, 2, 1, 4, 1.0, "",
80 GLXContext *glx_context;
82 trackball_state *trackball;
86 static Chesscreen *qs = NULL;
94 #define M_PI 3.14159265
99 static float MaterialShadow[] = {0.0, 0.0, 0.0, 0.3};
103 /** definition of white/black (orange/gray) colors */
104 GLfloat colors[2][3] =
112 /* i prefer silvertip */
113 GLfloat whites[WHITES][3] =
122 #include "chessgames.h"
127 void build_colors(void) {
130 int newwhite = oldwhite;
131 while(newwhite == oldwhite)
132 newwhite = random()%WHITES;
135 colors[0][0] = whites[oldwhite][0];
136 colors[0][1] = whites[oldwhite][1];
137 colors[0][2] = whites[oldwhite][2];
141 #define checkImageWidth 16
142 #define checkImageHeight 16
143 GLubyte checkImage[checkImageWidth][checkImageHeight][3];
144 GLuint piecetexture, boardtexture;
146 /* build piece texture */
147 void make_piece_texture(void) {
150 for (i = 0; i < checkImageWidth; i++) {
151 for (j = 0; j < checkImageHeight; j++) {
152 c = ((j%2) == 0 || i%2 == 0) ? 240 : 180+random()%16;
153 checkImage[i][j][0] = (GLubyte) c;
154 checkImage[i][j][1] = (GLubyte) c;
155 checkImage[i][j][2] = (GLubyte) c;
159 glGenTextures(1, &piecetexture);
160 glBindTexture(GL_TEXTURE_2D, piecetexture);
162 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
163 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
164 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
165 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
166 glTexImage2D(GL_TEXTURE_2D, 0, 3, checkImageWidth,
167 checkImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE,
171 /* build board texture (uniform noise in [180,180+50]) */
172 void make_board_texture(void) {
175 for (i = 0; i < checkImageWidth; i++) {
176 for (j = 0; j < checkImageHeight; j++) {
177 c = 180 + random()%51;
178 checkImage[i][j][0] = (GLubyte) c;
179 checkImage[i][j][1] = (GLubyte) c;
180 checkImage[i][j][2] = (GLubyte) c;
184 glGenTextures(1, &boardtexture);
185 glBindTexture(GL_TEXTURE_2D, boardtexture);
187 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
188 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
189 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
190 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
191 glTexImage2D(GL_TEXTURE_2D, 0, 3, checkImageWidth,
192 checkImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE,
197 int mpiece = 0, tpiece, steps = 0, done = 1;
198 double from[2], to[2];
200 int moving = 0, take = 0, mc = 0, count = 99, wire = 0;
203 /** handle X event (trackball) */
204 Bool chess_handle_event (ModeInfo *mi, XEvent *event) {
205 Chesscreen *c = &qs[MI_SCREEN(mi)];
207 if(event->xany.type == ButtonPress && event->xbutton.button == Button1) {
208 c->button_down_p = True;
209 gltrackball_start (c->trackball,
210 event->xbutton.x, event->xbutton.y,
211 MI_WIDTH (mi), MI_HEIGHT (mi));
214 else if(event->xany.type == ButtonRelease
215 && event->xbutton.button == Button1) {
216 c->button_down_p = False;
219 else if (event->xany.type == ButtonPress &&
220 (event->xbutton.button == Button4 ||
221 event->xbutton.button == Button5))
223 gltrackball_mousewheel (c->trackball, event->xbutton.button, 5,
224 !event->xbutton.state);
227 else if(event->xany.type == MotionNotify && c->button_down_p) {
228 gltrackball_track (c->trackball,
229 event->xmotion.x, event->xmotion.y,
230 MI_WIDTH (mi), MI_HEIGHT (mi));
237 GLfloat position[] = { 0.0, 5.0, 5.0, 1.0 };
238 GLfloat position2[] = { 5.0, 5.0, 5.0, 1.0 };
239 GLfloat diffuse2[] = {1.0, 1.0, 1.0, 1.0};
240 GLfloat ambient2[] = {0.7, 0.7, 0.7, 1.0};
241 GLfloat shininess[] = {60.0};
242 GLfloat specular[] = {0.4, 0.4, 0.4, 1.0};
244 /* configure lighting */
245 void setup_lights(void) {
246 glEnable(GL_LIGHTING);
247 glLightfv(GL_LIGHT0, GL_POSITION, position);
248 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse2);
251 /* glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient2); */
253 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
254 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
256 glLightfv(GL_LIGHT1, GL_SPECULAR, diffuse2);
257 glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse2);
262 void drawPieces(void) {
265 for(i = 0; i < BOARDSIZE; ++i) {
266 for(j = 0; j < BOARDSIZE; ++j) {
267 if(game.board[i][j]) {
268 int c = game.board[i][j]/PIECES;
269 glColor3fv(colors[c]);
270 glCallList(game.board[i][j]%PIECES);
273 glTranslatef(1.0, 0.0, 0.0);
276 glTranslatef(-1.0*BOARDSIZE, 0.0, 1.0);
279 glTranslatef(0.0, 0.0, -1.0*BOARDSIZE);
283 void drawPiecesShadow(void) {
286 for(i = 0; i < BOARDSIZE; ++i) {
287 for(j = 0; j < BOARDSIZE; ++j) {
288 if(game.board[i][j]) {
289 glColor4f(0.0, 0.0, 0.0, 0.4);
290 glCallList(game.board[i][j]%PIECES);
293 glTranslatef(1.0, 0.0, 0.0);
296 glTranslatef(-1.0*BOARDSIZE, 0.0, 1.0);
299 glTranslatef(0.0, 0.0, -1.0*BOARDSIZE);
302 /* draw a moving piece */
303 void drawMovingPiece(int shadow) {
304 int piece = mpiece % PIECES;
308 if(shadow) glColor4fv(MaterialShadow);
309 else glColor3fv(colors[mpiece/PIECES]);
311 /** assume a queening. should be more general */
312 if((mpiece == PAWN && fabs(to[0]) < 0.01) ||
313 (mpiece == BPAWN && fabs(to[0]-7.0) < 0.01)) {
314 glTranslatef(from[1]+steps*dx, 0.0, from[0]+steps*dz);
316 glColor4f(shadow ? MaterialShadow[0] : colors[mpiece/7][0],
317 shadow ? MaterialShadow[1] : colors[mpiece/7][1],
318 shadow ? MaterialShadow[2] : colors[mpiece/7][2],
319 (fabs(50.0-steps))/50.0);
321 piece = steps < 50 ? PAWN : QUEEN;
325 mpiece = mpiece == PAWN ? QUEEN : BQUEEN;
327 else if(mpiece % PIECES == KNIGHT) {
331 glTranslatef(steps < 50 ? from[1] : to[1], 0.0,
332 steps < 50 ? from[0] : to[0]);
335 ? (1.0 - steps / 10.0) : 100 - steps < 10
336 ? (1.0 - (100 - steps) / 10.0) : 0.0;
338 shine[0] = mult*shininess[0];
339 spec[0] = mult*specular[0];
340 spec[1] = mult*specular[1];
341 spec[2] = mult*specular[2];
343 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shine);
344 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spec);
346 glColor4f(shadow ? MaterialShadow[0] : colors[mpiece/7][0],
347 shadow ? MaterialShadow[1] : colors[mpiece/7][1],
348 shadow ? MaterialShadow[2] : colors[mpiece/7][2],
349 fabs(49-steps)/49.0);
351 glScalef(fabs(49-steps)/49.0, fabs(49-steps)/49.0, fabs(49-steps)/49.0);
354 glTranslatef(from[1]+steps*dx, 0.0, from[0]+steps*dz);
361 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
362 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
370 /** code to squish a taken piece */
371 void drawTakePiece(int shadow) {
375 glColor4f(shadow ? MaterialShadow[0] : colors[tpiece/7][0],
376 shadow ? MaterialShadow[1] : colors[tpiece/7][1],
377 shadow ? MaterialShadow[0] : colors[tpiece/7][2],
378 (100-1.6*steps)/100.0);
380 glTranslatef(to[1], 0.0, to[0]);
382 if(mpiece % PIECES == KNIGHT)
383 glScalef(1.0+steps/100.0, 1.0, 1.0+steps/100.0);
385 glScalef(1.0, 1 - steps/50.0 > 0.01 ? 1 - steps/50.0 : 0.01, 1.0);
386 glCallList(tpiece % 7);
395 void drawBoard(void) {
400 for(i = 0; i < BOARDSIZE; ++i)
401 for(j = 0; j < BOARDSIZE; ++j) {
402 double ma1 = (i+j)%2 == 0 ? mod*i : 0.0;
403 double mb1 = (i+j)%2 == 0 ? mod*j : 0.0;
404 double ma2 = (i+j)%2 == 0 ? mod*(i+1.0) : 0.01;
405 double mb2 = (i+j)%2 == 0 ? mod*(j+1.0) : 0.01;
407 /*glColor3fv(colors[(i+j)%2]);*/
408 glColor4f(colors[(i+j)%2][0], colors[(i+j)%2][1],
409 colors[(i+j)%2][2], 0.65);
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);
426 /* chop underneath board */
427 /* glColor3f(0, 0, 0); */
428 /* glNormal3f(0, -1, 0); */
429 /* glVertex3f(0, 0, BOARDSIZE); */
430 /* glVertex3f(0, 0, 0); */
431 /* glVertex3f(BOARDSIZE, 0, 0); */
432 /* glVertex3f(BOARDSIZE, 0, BOARDSIZE); */
436 void draw_pieces(int wire) {
438 glEnable(GL_TEXTURE_2D);
439 glBindTexture(GL_TEXTURE_2D, piecetexture);
440 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
442 glColor4f(0.5, 0.5, 0.5, 1.0);
443 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialShadow);
447 if(moving) drawMovingPiece(0);
448 if(take) drawTakePiece(0);
449 glDisable(GL_TEXTURE_2D);
452 void draw_shadow_pieces(int wire) {
454 glEnable(GL_TEXTURE_2D);
455 glBindTexture(GL_TEXTURE_2D, piecetexture);
456 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
460 if(moving) drawMovingPiece(shadows);
461 if(take) drawTakePiece(shadows);
462 glDisable(GL_TEXTURE_2D);
468 /* create a matrix that will project the desired shadow */
469 void shadowmatrix(GLfloat shadowMat[4][4],
470 GLfloat groundplane[4],
471 GLfloat lightpos[4]) {
474 /* find dot product between light position vector and ground plane normal */
475 dot = groundplane[X] * lightpos[X] +
476 groundplane[Y] * lightpos[Y] +
477 groundplane[Z] * lightpos[Z] +
478 groundplane[W] * lightpos[W];
480 shadowMat[0][0] = dot - lightpos[X] * groundplane[X];
481 shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y];
482 shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z];
483 shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W];
485 shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X];
486 shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y];
487 shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z];
488 shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W];
490 shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X];
491 shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y];
492 shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z];
493 shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W];
495 shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X];
496 shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y];
497 shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z];
498 shadowMat[3][3] = dot - lightpos[W] * groundplane[W];
501 GLfloat ground[4] = {0.0, 1.0, 0.0, -0.00001};
503 /** reflectionboard */
504 void draw_reflections(void) {
507 glEnable(GL_STENCIL_TEST);
508 glStencilFunc(GL_ALWAYS, 1, 1);
509 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
510 glColorMask(0,0,0,0);
511 glDisable(GL_CULL_FACE);
513 glDisable(GL_DEPTH_TEST);
516 /* only draw white squares */
517 for(i = 0; i < BOARDSIZE; ++i) {
518 for(j = (BOARDSIZE+i) % 2; j < BOARDSIZE; j += 2) {
519 glVertex3f(i, 0.0, j + 1.0);
520 glVertex3f(i + 1.0, 0.0, j + 1.0);
521 glVertex3f(i + 1.0, 0.0, j);
522 glVertex3f(i, 0.0, j);
526 glEnable(GL_DEPTH_TEST);
528 glColorMask(1, 1, 1, 1);
529 glStencilFunc(GL_EQUAL, 1, 1);
530 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
533 glScalef(1.0, -1.0, 1.0);
534 glTranslatef(0.5, 0.0, 0.5);
536 glLightfv(GL_LIGHT0, GL_POSITION, position);
540 glDisable(GL_STENCIL_TEST);
541 glLightfv(GL_LIGHT0, GL_POSITION, position);
543 glEnable(GL_CULL_FACE);
545 glColorMask(1,1,1,1);
548 /** draws the scene */
549 void display(Chesscreen *c) {
550 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
552 glMatrixMode(GL_MODELVIEW);
555 /** setup perspectif */
556 glTranslatef(0.0, 0.0, -1.5*BOARDSIZE);
557 glRotatef(30.0, 1.0, 0.0, 0.0);
558 gltrackball_rotate (c->trackball);
559 glRotatef(theta*100, 0.0, 1.0, 0.0);
560 glTranslatef(-0.5*BOARDSIZE, 0.0, -0.5*BOARDSIZE);
562 position[0] = 4.0 + 1.0*-sin(theta*100*M_PI/180.0);
563 position[2] = 4.0 + 1.0*cos(theta*100*M_PI/180.0);
566 position2[0] = 4.0 + 8.0*-sin(theta*100*M_PI/180.0);
567 position2[2] = 4.0 + 8.0*cos(theta*100*M_PI/180.0);
570 glEnable(GL_LIGHTING);
571 glLightfv(GL_LIGHT0, GL_POSITION, position);
572 glLightfv(GL_LIGHT1, GL_POSITION, position2);
576 /** draw board, pieces */
578 glEnable(GL_LIGHTING);
579 glEnable(GL_COLOR_MATERIAL);
581 if(reflections && !wire) {
586 glEnable(GL_TEXTURE_2D);
587 glBindTexture(GL_TEXTURE_2D, boardtexture);
589 glDisable(GL_TEXTURE_2D);
594 shadowmatrix(m, ground, position);
596 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialShadow);
598 glDisable(GL_LIGHTING);
599 glDisable(GL_DEPTH_TEST);
601 /* display ant shadow */
603 glTranslatef(0.0, 0.001, 0.0);
605 glTranslatef(0.5, 0.01, 0.5);
606 draw_shadow_pieces(wire);
609 glEnable(GL_LIGHTING);
611 glEnable(GL_DEPTH_TEST);
620 glTranslatef(0.5, 0.0, 0.5);
624 glDisable(GL_COLOR_MATERIAL);
625 glDisable(GL_LIGHTING);
628 if (!c->button_down_p)
632 /** reshape handler */
633 void reshape_chess(ModeInfo *mi, int width, int height) {
634 GLfloat h = (GLfloat) height / (GLfloat) width;
635 glViewport(0,0, width, height);
636 glMatrixMode(GL_PROJECTION);
638 gluPerspective(45, 1/h, 2.0, 30.0);
639 glMatrixMode(GL_MODELVIEW);
642 /** initialization handler */
643 void init_chess(ModeInfo *mi) {
645 int screen = MI_SCREEN(mi);
646 wire = MI_IS_WIREFRAME(mi);
649 !(qs = (Chesscreen *) calloc(MI_NUM_SCREENS(mi), sizeof(Chesscreen))))
653 c->window = MI_WINDOW(mi);
654 c->trackball = gltrackball_init ();
656 if((c->glx_context = init_GL(mi)))
657 reshape_chess(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
661 glClearColor(0.0, 0.0, 0.0, 0.0);
664 glDepthFunc(GL_LEQUAL);
666 glEnable(GL_CULL_FACE);
669 make_piece_texture();
670 make_board_texture();
676 glColorMaterial(GL_FRONT, GL_DIFFUSE);
677 glShadeModel(smooth ? GL_SMOOTH : GL_FLAT);
678 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
679 glEnable(GL_DEPTH_TEST);
682 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
687 /** does dirty work drawing scene, moving pieces */
688 void draw_chess(ModeInfo *mi) {
689 Chesscreen *c = &qs[MI_SCREEN(mi)];
690 Window w = MI_WINDOW(mi);
691 Display *disp = MI_DISPLAY(mi);
696 glXMakeCurrent(disp, w, *(c->glx_context));
698 /** code for moving a piece */
699 if(moving && ++steps == 100) {
700 moving = count = steps = take = 0;
701 game.board[game.moves[mc][2]][game.moves[mc][3]] = mpiece;
704 if(mc == game.movecount) {
712 mpiece = game.board[game.moves[mc][0]][game.moves[mc][1]];
713 game.board[game.moves[mc][0]][game.moves[mc][1]] = NONE;
715 if((tpiece = game.board[game.moves[mc][2]][game.moves[mc][3]])) {
716 game.board[game.moves[mc][2]][game.moves[mc][3]] = NONE;
720 from[0] = game.moves[mc][0];
721 from[1] = game.moves[mc][1];
722 to[0] = game.moves[mc][2];
723 to[1] = game.moves[mc][3];
725 dz = (to[0] - from[0]) / 100;
726 dx = (to[1] - from[1]) / 100;
731 int newgame = oldgame;
732 while(newgame == oldgame)
733 newgame = random()%GAMES;
736 mod = 0.6 + (random()%20)/10.0;
740 game = games[oldgame];
753 glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION,
754 done == 1 ? 1.0+0.1*count : 100.0/count);
755 glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION,
756 done == 1 ? 1.0+0.1*count : 100.0/count);
757 glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.14);
758 glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.14);
763 if(mi->fps_p) do_fps(mi);
765 glXSwapBuffers(disp, w);
769 void release_chess(ModeInfo *mi) {