281f5a98dc9e2bdffc7946b84a07f02019242112
[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 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 #include <X11/Intrinsic.h>
19
20 #ifdef STANDALONE
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
28
29 #define DEFAULTS       "*delay:       20000       \n" \
30                        "*showFPS:       False       \n" \
31                        "*wireframe:     False     \n"   \
32                        "*reflections:   True     \n"    \
33
34 # include "xlockmore.h"
35
36 #else
37 # include "xlock.h"
38 #endif
39
40 #ifdef USE_GL
41
42 #include <GL/glu.h>
43 #include "gltrackball.h"
44 #include "chessmodels.h"
45
46 #undef countof
47 #define countof(x) (sizeof((x))/sizeof((*x)))
48
49 static XrmOptionDescRec opts[] = {
50   {"+rotate", ".chess.rotate", XrmoptionNoArg, (caddr_t) "false" },
51   {"-rotate", ".chess.rotate", XrmoptionNoArg, (caddr_t) "true" },
52   {"+reflections", ".chess.reflections", XrmoptionNoArg, (caddr_t) "false" },
53   {"-reflections", ".chess.reflections", XrmoptionNoArg, (caddr_t) "true" },
54   {"+smooth", ".chess.smooth", XrmoptionNoArg, (caddr_t) "false" },
55   {"-smooth", ".chess.smooth", XrmoptionNoArg, (caddr_t) "true" },
56 };
57
58 int rotate, reflections, smooth;
59
60 static argtype vars[] = {
61   {&rotate,      "rotate",      "Rotate",      "True", t_Bool},
62   {&reflections, "reflections", "Reflections", "True", t_Bool},
63   {&smooth,      "smooth",      "Smooth",      "True", t_Bool},
64 };
65
66 ModeSpecOpt chess_opts = {countof(opts), opts, countof(vars), vars, NULL};
67
68 #ifdef USE_MODULES
69 ModStruct   chess_description =
70 {"chess", "init_chess", "draw_chess", "release_chess",
71  "draw_chess", "init_chess", NULL, &chess_opts,
72  1000, 1, 2, 1, 4, 1.0, "",
73  "Chess", 0, NULL};
74
75 #endif
76
77 typedef struct {
78   GLXContext *glx_context;
79   Window window;
80   trackball_state *trackball;
81   Bool button_down_p;
82 } Chesscreen;
83
84 static Chesscreen *qs = NULL;
85
86 #include <math.h>
87 #include <sys/time.h>
88 #include <stdio.h>
89 #include <stdlib.h>
90
91 #ifndef M_PI
92 #define M_PI 3.14159265
93 #endif
94
95 #define BOARDSIZE 8
96
97 /** definition of white/black (orange/gray) colors */
98 GLfloat colors[2][3] = 
99   { 
100     {1.0, 0.5, 0.0},
101     {0.5, 0.5, 0.5},
102   };
103
104 #define WHITES 5
105
106 /* i prefer silvertip */
107 GLfloat whites[WHITES][3] = 
108   {
109     {1.0, 0.5, 0.0},
110     {0.9, 0.6, 0.9},
111     {0.43, 0.54, 0.76},
112     {0.8, 0.8, 0.8},
113     {0.15, 0.77, 0.54},
114   };
115
116 #include "chessgames.h"
117
118 ChessGame game;
119 int oldwhite = -1;
120
121 void build_colors(void) {
122
123   /* find new white */
124   int newwhite = oldwhite;
125   while(newwhite == oldwhite)
126     newwhite = random()%WHITES;
127   oldwhite = newwhite;
128
129   colors[0][0] = whites[oldwhite][0];
130   colors[0][1] = whites[oldwhite][1];
131   colors[0][2] = whites[oldwhite][2];
132 }
133
134 /* yay its c */
135 int mpiece = 0, tpiece, steps = 0, done = 1;
136 double from[2], to[2];
137 double dx, dz;
138 int moving = 0, take = 0, mc = 0, count = 99, wire = 0;
139
140 /** handle X event (trackball) */
141 Bool chess_handle_event (ModeInfo *mi, XEvent *event) {
142   Chesscreen *c = &qs[MI_SCREEN(mi)];
143
144   if(event->xany.type == ButtonPress && event->xbutton.button & Button1) {
145     c->button_down_p = True;
146     gltrackball_start (c->trackball,
147                        event->xbutton.x, event->xbutton.y,
148                        MI_WIDTH (mi), MI_HEIGHT (mi));
149     return True;
150   }
151   else if(event->xany.type == ButtonRelease 
152           && event->xbutton.button & Button1) {
153     c->button_down_p = False;
154     return True;
155   }
156   else if(event->xany.type == MotionNotify && c->button_down_p) {
157     gltrackball_track (c->trackball,
158                        event->xmotion.x, event->xmotion.y,
159                        MI_WIDTH (mi), MI_HEIGHT (mi));
160     return True;
161   }
162   
163   return False;
164 }
165
166 GLfloat position[] = { 0.0, 5.0, 5.0, 1.0 };
167 GLfloat position2[] = { 5.0, 5.0, 5.0, 1.0 };
168 GLfloat diffuse2[] = {1.0, 1.0, 1.0, 1.0};
169 GLfloat ambient2[] = {0.6, 0.6, 0.6, 1.0};
170 GLfloat shininess[] = {60.0};
171 GLfloat specular[] = {0.4, 0.4, 0.4, 1.0};
172
173 /* configure lighting */
174 void setup_lights(void) {
175   glEnable(GL_LIGHTING);
176   glLightfv(GL_LIGHT0, GL_POSITION, position);
177   glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse2);
178   glEnable(GL_LIGHT0);
179
180 /*   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient2); */
181
182   glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
183   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
184
185   glLightfv(GL_LIGHT1, GL_SPECULAR, diffuse2);
186   glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse2);
187   glEnable(GL_LIGHT1);
188 }
189
190 /** draw pieces */
191 void drawPieces(void) {
192   int i, j;
193
194   for(i = 0; i < BOARDSIZE; ++i) {
195     for(j = 0; j < BOARDSIZE; ++j) {
196       if(game.board[i][j]) {    
197         int c = game.board[i][j]/PIECES;
198         glColor3fv(colors[c]);
199         glCallList(game.board[i][j]%PIECES);
200       }
201       
202       glTranslatef(1.0, 0.0, 0.0);
203     }
204     
205     glTranslatef(-1.0*BOARDSIZE, 0.0, 1.0);
206   }
207
208   glTranslatef(0.0, 0.0, -1.0*BOARDSIZE);
209 }
210
211 /** draw a moving piece */
212 void drawMovingPiece(void) {
213   int piece = mpiece % PIECES;
214
215   glPushMatrix();
216   glColor3fv(colors[mpiece/PIECES]);
217
218   /** assume a queening.  should be more general */
219   if((mpiece == PAWN  && fabs(to[0]) < 0.01) || 
220      (mpiece == BPAWN && fabs(to[0]-7.0) < 0.01)) {
221     glTranslatef(from[1]+steps*dx, 0.0, from[0]+steps*dz);
222     glColor4f(colors[mpiece/7][0], colors[mpiece/7][1], colors[mpiece/7][2],
223               (fabs(50.0-steps))/50.0);
224     piece = steps < 50 ? PAWN : QUEEN;
225
226     /* what a kludge */
227     if(steps == 99)
228       mpiece = mpiece == PAWN ? QUEEN : BQUEEN;
229   }
230   else if(mpiece % PIECES == KNIGHT) {
231     GLfloat shine[1];
232     GLfloat spec[4];
233     GLfloat mult;
234     glTranslatef(steps < 50 ? from[1] : to[1], 0.0, 
235                  steps < 50 ? from[0] : to[0]);
236
237     mult = steps < 10 
238       ? (1.0 - steps / 10.0) 
239       : 100 - steps < 10 
240       ? specular[0] * (1.0 - (100 - steps) / 10.0) 
241       : 0.0;
242
243     shine[0] = 0.0;/*mult*shininess[0];*/
244     spec[0] = mult*specular[0];
245     spec[1] = mult*specular[1];
246     spec[2] = mult*specular[2];
247     spec[3] = 1.0;
248     glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shine);
249     glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, spec);
250
251     glColor4f(colors[mpiece/7][0], colors[mpiece/7][1], colors[mpiece/7][2],
252               fabs(49-steps)/49.0);
253     glScalef(fabs(49-steps)/49.0, fabs(49-steps)/49.0, fabs(49-steps)/49.0);
254   }
255   else
256     glTranslatef(from[1]+steps*dx, 0.0, from[0]+steps*dz);
257
258   if(!wire)
259     glEnable(GL_BLEND);
260   
261   glCallList(piece);
262
263   glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
264   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
265
266   glPopMatrix();
267
268   if(!wire)
269     glDisable(GL_BLEND);
270 }
271
272 /** code to squish a taken piece */
273 void drawTakePiece(void) {
274   if(!wire)
275     glEnable(GL_BLEND);
276
277   glColor4f(colors[tpiece/7][0], colors[tpiece/7][1], colors[tpiece/7][2],
278             (100-1.6*steps)/100.0);
279
280   glTranslatef(to[1], 0.0, to[0]);
281   
282   if(mpiece % PIECES == KNIGHT)
283     glScalef(1.0+steps/100.0, 1.0, 1.0+steps/100.0);
284   else
285     glScalef(1.0, 1 - steps/50.0 > 0.01 ? 1 - steps/50.0 : 0.01, 1.0);
286   glCallList(tpiece % 7);
287   
288   if(!wire)
289     glDisable(GL_BLEND);
290 }
291
292 /** draw board */
293 void drawBoard(void) {
294   int i, j;
295
296   glBegin(GL_QUADS);
297
298   for(i = 0; i < BOARDSIZE; ++i)
299     for(j = 0; j < BOARDSIZE; ++j) {
300       /*glColor3fv(colors[(i+j)%2]);*/
301       glColor4f(colors[(i+j)%2][0], colors[(i+j)%2][1],
302                 colors[(i+j)%2][2], 0.65);
303       glNormal3f(0.0, 1.0, 0.0);
304       glVertex3f(i, 0.0, j + 1.0);
305       glVertex3f(i + 1.0, 0.0, j + 1.0);
306       glVertex3f(i + 1.0, 0.0, j);
307       glVertex3f(i, 0.0, j);
308     }
309
310   /* chop underneath board */
311 /*   glColor3f(0, 0, 0); */
312 /*   glNormal3f(0, -1, 0); */
313 /*   glVertex3f(0,         0,  BOARDSIZE); */
314 /*   glVertex3f(0,         0,  0); */
315 /*   glVertex3f(BOARDSIZE, 0,  0); */
316 /*   glVertex3f(BOARDSIZE, 0,  BOARDSIZE); */
317   glEnd();
318 }
319
320 double theta = 0.0;
321
322 void draw_pieces(void) {
323   drawPieces();
324   if(moving) drawMovingPiece();
325   if(take) drawTakePiece();
326 }
327
328 /** reflectionboard */
329 void draw_reflections(void) {
330   int i, j;
331
332   glEnable(GL_STENCIL_TEST);
333   glStencilFunc(GL_ALWAYS, 1, 1);
334   glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
335   glColorMask(0,0,0,0);
336   glDisable(GL_CULL_FACE);
337
338   glDisable(GL_DEPTH_TEST);
339   glBegin(GL_QUADS);
340
341   /* only draw white squares */
342   for(i = 0; i < BOARDSIZE; ++i) {
343     for(j = (BOARDSIZE+i) % 2; j < BOARDSIZE; j += 2) {
344       glVertex3f(i, 0.0, j + 1.0);
345       glVertex3f(i + 1.0, 0.0, j + 1.0);
346       glVertex3f(i + 1.0, 0.0, j);
347       glVertex3f(i, 0.0, j);
348     }
349   }
350   glEnd();
351   glEnable(GL_DEPTH_TEST);
352
353   glColorMask(1, 1, 1, 1);
354   glStencilFunc(GL_EQUAL, 1, 1);
355   glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
356   
357   glPushMatrix(); 
358   glScalef(1.0, -1.0, 1.0);
359   glTranslatef(0.5, 0.0, 0.5);
360
361   glLightfv(GL_LIGHT0, GL_POSITION, position);
362   draw_pieces();
363   glPopMatrix();
364   
365   glDisable(GL_STENCIL_TEST);
366   glLightfv(GL_LIGHT0, GL_POSITION, position);
367
368   glEnable(GL_CULL_FACE);
369   glCullFace(GL_BACK);
370   glColorMask(1,1,1,1);
371 }
372
373 /** draws the scene */
374 void display(Chesscreen *c) {
375   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
376
377   glMatrixMode(GL_MODELVIEW);
378   glLoadIdentity();
379
380   /** setup perspectif */
381   glTranslatef(0.0, 0.0, -1.5*BOARDSIZE);
382   glRotatef(30.0, 1.0, 0.0, 0.0);
383   gltrackball_rotate (c->trackball);
384   glRotatef(theta*100, 0.0, 1.0, 0.0);
385   glTranslatef(-0.5*BOARDSIZE, 0.0, -0.5*BOARDSIZE);
386
387   position[0] = 4.0 + 1.0*-sin(theta*100*M_PI/180.0);
388   position[2] = 4.0 + 1.0*cos(theta*100*M_PI/180.0);
389   position[1] = 8.0;
390
391   position2[0] = 4.0 + 8.0*-sin(theta*100*M_PI/180.0);
392   position2[2] = 4.0 + 8.0*cos(theta*100*M_PI/180.0);
393
394   glEnable(GL_LIGHTING);
395   glLightfv(GL_LIGHT0, GL_POSITION, position);
396   glLightfv(GL_LIGHT1, GL_POSITION, position2);
397   glEnable(GL_LIGHT0);
398
399   /** draw board, pieces */
400   if(!wire) {
401     glEnable(GL_LIGHTING);
402     glEnable(GL_COLOR_MATERIAL);
403
404     if(reflections) {
405       draw_reflections();
406       glEnable(GL_BLEND);
407     }
408
409     drawBoard();
410
411     if(reflections)
412       glDisable(GL_BLEND);
413   }
414   else
415     drawBoard();
416  
417   glTranslatef(0.5, 0.0, 0.5);
418   draw_pieces();
419
420   if(!wire) {
421     glDisable(GL_COLOR_MATERIAL);
422     glDisable(GL_LIGHTING);
423   }
424
425   if (!c->button_down_p)
426     theta += .002;
427 }
428
429 /** reshape handler */
430 void reshape_chess(ModeInfo *mi, int width, int height) {
431   GLfloat h = (GLfloat) height / (GLfloat) width;
432   glViewport(0,0, width, height);
433   glMatrixMode(GL_PROJECTION);
434   glLoadIdentity();
435   gluPerspective(45, 1/h, 2.0, 30.0);
436   glMatrixMode(GL_MODELVIEW);
437 }
438
439 /** initialization handler */
440 void init_chess(ModeInfo *mi) {
441   Chesscreen *c;
442   int screen = MI_SCREEN(mi);
443   wire = MI_IS_WIREFRAME(mi);
444
445   if(!qs && 
446      !(qs = (Chesscreen *) calloc(MI_NUM_SCREENS(mi), sizeof(Chesscreen))))
447     return;
448   
449   c = &qs[screen];
450   c->window = MI_WINDOW(mi);
451   c->trackball = gltrackball_init ();
452   
453   if((c->glx_context = init_GL(mi)))
454     reshape_chess(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
455   else
456     MI_CLEARWINDOW(mi);
457
458   glClearColor(0.0, 0.0, 0.0, 0.0);
459
460   glDepthFunc(GL_LEQUAL);
461   glClearStencil(0);
462   glEnable(GL_CULL_FACE);
463   glCullFace(GL_BACK);
464
465   gen_model_lists();
466
467   if(!wire) {
468     setup_lights();
469     glColorMaterial(GL_FRONT, GL_DIFFUSE);
470     glShadeModel(smooth ? GL_SMOOTH : GL_FLAT);
471     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
472     glEnable(GL_DEPTH_TEST);
473   }
474   else
475     glPolygonMode(GL_FRONT, GL_LINE);
476 }
477
478 int oldgame = -1;
479
480 /** does dirty work drawing scene, moving pieces */
481 void draw_chess(ModeInfo *mi) {
482   Chesscreen *c = &qs[MI_SCREEN(mi)];
483   Window w = MI_WINDOW(mi);
484   Display *disp = MI_DISPLAY(mi);
485
486   if(!c->glx_context)
487     return;
488
489   glXMakeCurrent(disp, w, *(c->glx_context));
490
491   /** code for moving a piece */
492   if(moving && ++steps == 100) {
493     moving = count = steps = take = 0;
494     game.board[game.moves[mc][2]][game.moves[mc][3]] = mpiece;
495     ++mc;
496     
497     if(mc == game.movecount) {
498       done = 1;
499       mc = 0;
500     }
501   }
502
503   if(++count == 100) {
504     if(!done) {
505       mpiece = game.board[game.moves[mc][0]][game.moves[mc][1]];
506       game.board[game.moves[mc][0]][game.moves[mc][1]] = NONE;
507       
508       if((tpiece = game.board[game.moves[mc][2]][game.moves[mc][3]])) {
509         game.board[game.moves[mc][2]][game.moves[mc][3]] = NONE;
510         take = 1;
511       }
512       
513       from[0] = game.moves[mc][0];
514       from[1] = game.moves[mc][1];
515       to[0] = game.moves[mc][2];
516       to[1] = game.moves[mc][3];
517       
518       dz = (to[0] - from[0]) / 100;
519       dx = (to[1] - from[1]) / 100;
520       steps = 0;
521       moving = 1;
522     }
523     else if(done == 1) {
524       int newgame = oldgame;
525       while(newgame == oldgame)
526         newgame = random()%GAMES;
527
528       /* same old game */
529       oldgame = newgame;
530       game = games[oldgame];
531       build_colors();
532       done = 2;
533       count = 0;
534     }
535     else {
536       done = 0;
537       count = 0;
538     }
539   }
540
541   /* set lighting */
542   if(done) {
543     glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 
544              done == 1 ? 1.0+0.1*count : 100.0/count);
545     glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 
546              done == 1 ? 1.0+0.1*count : 100.0/count);
547     glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.15);
548     glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.15);
549   }
550
551   display(c);
552
553   if(mi->fps_p) do_fps(mi);
554   glFinish(); 
555   glXSwapBuffers(disp, w);
556 }
557
558 /** bust it */
559 void release_chess(ModeInfo *mi) {
560   if(qs)
561     free((void *) qs);
562
563   FreeAllGL(MI);
564 }
565
566 #endif