1/*
2	File:		MBCBoardViewDraw.mm
3	Contains:	Draw chess board
4	Copyright:	� 2002-2012 by Apple Inc., all rights reserved.
5
6	Derived from glChess, Copyright © 2002 Robert Ancell and Michael Duelli
7	Permission granted to Apple to relicense under the following terms:
8
9	IMPORTANT: This Apple software is supplied to you by Apple Computer,
10	Inc.  ("Apple") in consideration of your agreement to the following
11	terms, and your use, installation, modification or redistribution of
12	this Apple software constitutes acceptance of these terms.  If you do
13	not agree with these terms, please do not use, install, modify or
14	redistribute this Apple software.
15
16	In consideration of your agreement to abide by the following terms,
17	and subject to these terms, Apple grants you a personal, non-exclusive
18	license, under Apple's copyrights in this original Apple software (the
19	"Apple Software"), to use, reproduce, modify and redistribute the
20	Apple Software, with or without modifications, in source and/or binary
21	forms; provided that if you redistribute the Apple Software in its
22	entirety and without modifications, you must retain this notice and
23	the following text and disclaimers in all such redistributions of the
24	Apple Software.  Neither the name, trademarks, service marks or logos
25	of Apple Inc. may be used to endorse or promote products
26	derived from the Apple Software without specific prior written
27	permission from Apple.  Except as expressly stated in this notice, no
28	other rights or licenses, express or implied, are granted by Apple
29	herein, including but not limited to any patent rights that may be
30	infringed by your derivative works or by other works in which the
31	Apple Software may be incorporated.
32
33	The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
34	MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
35	THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND
36	FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
37	USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
38
39	IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
40	INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
41	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
42	PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
43	REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE,
44	HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING
45	NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
46	ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47*/
48
49#import "MBCBoardViewDraw.h"
50#import "MBCBoardViewModels.h"
51#import "MBCBoardViewTextures.h"
52
53#import <math.h>
54#import <OpenGL/glu.h>
55#import <algorithm>
56#import <sys/time.h>
57#import <vector>
58
59using std::min;
60
61@implementation MBCDrawStyle
62
63- (id) init
64{
65	fTexture	= 0;
66
67	return self;
68}
69
70- (id) initWithTexture:(GLuint)tex
71{
72	fTexture	= tex;
73	fDiffuse	= 1.0f;
74	fSpecular	= 0.2f;
75	fShininess	= 5.0f;
76	fAlpha		= 1.0f;
77
78	return self;
79}
80
81- (void) unloadTexture
82{
83	if (fTexture)
84		glDeleteTextures(1, &fTexture);
85}
86
87- (void) startStyle:(float)alpha
88{
89	GLfloat white_texture_color[4] 	=
90		{fDiffuse, fDiffuse, fDiffuse, fAlpha*alpha};
91	GLfloat emission_color[4] 		=
92		{0.0f, 0.0f, 0.0f, fAlpha*alpha};
93	GLfloat specular_color[4] 		=
94		{fSpecular, fSpecular, fSpecular, fAlpha*alpha};
95
96	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
97	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, white_texture_color);
98	glMaterialfv(GL_FRONT, GL_EMISSION, emission_color);
99	glMaterialfv(GL_FRONT, GL_SPECULAR, specular_color);
100	glMaterialf(GL_FRONT, GL_SHININESS, fShininess);
101	glBindTexture(GL_TEXTURE_2D, fTexture);
102}
103
104@end
105
106@implementation MBCBoardView ( Draw )
107
108- (void) setupPerspective
109{
110    NSRect bounds             = [self convertRectToBacking:[self bounds]];
111    GLint opaque = NO;
112    [[self openGLContext] setValues:&opaque
113							  forParameter:NSOpenGLCPSurfaceOpacity];
114    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
115
116	/* Stuff you can't do without */
117	glEnable(GL_DEPTH_TEST);
118	glEnable(GL_CULL_FACE);
119	glEnable(GL_NORMALIZE);
120
121	/* Textures */
122	glEnable(GL_TEXTURE_2D);
123
124	/* The lighting */
125	glEnable(GL_LIGHTING);
126
127	glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
128	glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
129	glShadeModel(GL_SMOOTH);
130
131	glDisable(GL_FOG);
132
133    const float w               = bounds.size.width;
134    const float h               = bounds.size.height*1.1f*(fVariant==kVarCrazyhouse ? 1.15f : 1.0f);
135    const float kAspect         = std::max(1.0f, h / w);
136    const float kDistance       = 300.0f;
137    const float kBoardSize      = kAspect*50.0f;
138    const float kDeg2Rad        = M_PI / 180.0f;
139    const float kRad2Deg        = 180.0f / M_PI;
140    const float kAngleOfView    = 2.0f * atan2(kBoardSize, kDistance) * kRad2Deg;
141
142	glViewport(0, 0, (GLsizei)(bounds.size.width), (GLsizei)(bounds.size.height));
143
144	glMatrixMode(GL_MODELVIEW);
145	glLoadIdentity();
146	glMatrixMode(GL_PROJECTION);
147	glLoadIdentity();
148
149    gluPerspective(kAngleOfView, bounds.size.width/bounds.size.height,
150				   10.0, 1000.0);
151
152	glMatrixMode(GL_TEXTURE);
153	glLoadIdentity();
154	glScalef(1.0f, -1.0f, 0.0f);
155
156	glMatrixMode(GL_MODELVIEW);
157
158	float	cameraY = kDistance * sin(fElevation * kDeg2Rad);
159	float   cameraXZ= kDistance * cos(fElevation * kDeg2Rad);
160	float	cameraX = cameraXZ  * sin(fAzimuth * kDeg2Rad);
161	float	cameraZ = cameraXZ  *-cos(fAzimuth * kDeg2Rad);
162
163	gluLookAt(cameraX, cameraY, cameraZ,
164			  0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
165
166	fNeedPerspective	= false;
167}
168
169- (void) drawBoard:(BOOL)overReflection
170{
171	int x, y, color;
172
173	//
174	// We want variation in the squares, not psychedelic effects
175	//
176	srandom(1);
177
178	glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_COLOR_BUFFER_BIT);
179
180	//
181	// Blend edges of squares
182	//
183	if (overReflection)
184		glDisable(GL_DEPTH_TEST);
185	glEnable(GL_BLEND);
186	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
187	glEnable(GL_LINE_SMOOTH);
188	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
189	// glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, !overReflection);
190
191	glNormal3f(0.0f, 1.0f, 0.0f);
192
193	for (x = 0; x < 8; x++) {
194		for (y = 0; y < 8; y++) {
195			/* Get board piece color */
196			color = (x % 2 == y % 2);
197
198			[fBoardDrawStyle[color]
199							startStyle:overReflection
200							? 1.0f-fBoardReflectivity
201							: 1.0f];
202
203			float r = random()/8589934588.0f; /* 4*(2**31-1) */
204
205			/* draw one square */
206			glBegin(GL_TRIANGLE_STRIP);
207			glTexCoord2f(0.0f+r, 0.0f+r);
208			glVertex3f(x * 10.0f - 40.0f, 0.0f, 40.0f - y * 10.0f);
209			glTexCoord2f(0.5f+r, 0.0f+r);
210			glVertex3f(x * 10.0f - 30.0f, 0.0f, 40.0f - y * 10.0f);
211			glTexCoord2f(0.0f+r, 0.5f+r);
212			glVertex3f(x * 10.0f - 40.0f, 0.0f, 30.0f - y * 10.0f);
213			glTexCoord2f(0.5f+r, 0.5f+r);
214			glVertex3f(x * 10.0f - 30.0f, 0.0f, 30.0f - y * 10.0f);
215			glEnd();
216
217#if 0
218			if (!overReflection) {
219				glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
220				glBegin(GL_QUADS);
221				glTexCoord2f(0.0f+r, 0.0f+r);
222				glVertex3f(x * 10.0f - 40.0f, 0.0f, 40.0f - y * 10.0f);
223				glTexCoord2f(0.5f+r, 0.0f+r);
224				glVertex3f(x * 10.0f - 30.0f, 0.0f, 40.0f - y * 10.0f);
225				glTexCoord2f(0.5f+r, 0.5f+r);
226				glVertex3f(x * 10.0f - 30.0f, 0.0f, 30.0f - y * 10.0f);
227				glTexCoord2f(0.0f+r, 0.5f+r);
228				glVertex3f(x * 10.0f - 40.0f, 0.0f, 30.0f - y * 10.0f);
229				glEnd();
230				glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
231			}
232#endif
233		}
234	}
235
236	if (overReflection) {
237		glPopAttrib();
238
239		return;
240	}
241
242
243	//
244	// Draw border
245	//
246	const float IB = kBoardRadius; 		// Inside border
247	const float OB = IB+kBorderWidth; 	// Outside border
248	const float DP =  5.0f;				// Depth
249	const float TO = 0.5f*(1.0f - IB/OB); 	// Texture offset
250
251	[fBorderDrawStyle startStyle:1.0f];
252
253	//
254	// Front
255	//
256	glBegin(GL_TRIANGLE_STRIP);
257	glNormal3f(0.0f, 1.0f, 0.0f);
258	glTexCoord2f(0.0f, 0.0f);
259	glVertex3f(-OB, 0.0f, +OB);
260	glTexCoord2f(TO, 0.0f);
261	glVertex3f(-IB, 0.0f, +OB);
262	glTexCoord2f(TO, 1.0f);
263	glVertex3f(-IB, 0.0f, +IB);
264	glTexCoord2f(1.0f-TO, 0.0f);
265	glVertex3f(+IB, 0.0f, +OB);
266	glTexCoord2f(1.0f-TO, 1.0f);
267	glVertex3f(+IB, 0.0f, +IB);
268	glTexCoord2f(1.0f, 0.0f);
269	glVertex3f(+OB, 0.0f, +OB);
270	glEnd();
271	glBegin(GL_TRIANGLE_STRIP);
272	glNormal3f(0.0f, 0.0f, 1.0f);
273	glTexCoord2f(0.0f, 1.0f);
274	glVertex3f(-OB, 0.0f, +OB);
275	glTexCoord2f(0.0f, 0.0f);
276	glVertex3f(-OB,  -DP, +OB);
277	glTexCoord2f(1.0f, 1.0f);
278	glVertex3f(+OB, 0.0f, +OB);
279	glTexCoord2f(1.0f, 0.0f);
280	glVertex3f(+OB,  -DP, +OB);
281	glEnd();
282	//
283	// Back
284	//
285	glBegin(GL_TRIANGLE_STRIP);
286	glNormal3f(0.0f, 1.0f, 0.0f);
287	glTexCoord2f(0.0f, 0.0f);
288	glVertex3f(-OB, 0.0f, -OB);
289	glTexCoord2f(TO, 1.0f);
290	glVertex3f(-IB, 0.0f, -IB);
291	glTexCoord2f(TO, 0.0f);
292	glVertex3f(-IB, 0.0f, -OB);
293	glTexCoord2f(1.0f-TO, 1.0f);
294	glVertex3f(+IB, 0.0f, -IB);
295	glTexCoord2f(1.0f-TO, 0.0f);
296	glVertex3f(+IB, 0.0f, -OB);
297	glTexCoord2f(1.0f, 0.0f);
298	glVertex3f(+OB, 0.0f, -OB);
299	glEnd();
300	glBegin(GL_TRIANGLE_STRIP);
301	glNormal3f(0.0f, 0.0f, -1.0f);
302	glTexCoord2f(0.0f, 1.0f);
303	glVertex3f(-OB, 0.0f, -OB);
304	glTexCoord2f(1.0f, 1.0f);
305	glVertex3f(+OB, 0.0f, -OB);
306	glTexCoord2f(0.0f, 0.0f);
307	glVertex3f(-OB,  -DP, -OB);
308	glTexCoord2f(1.0f, 0.0f);
309	glVertex3f(+OB,  -DP, -OB);
310	glEnd();
311	//
312	// Left
313	//
314	glBegin(GL_TRIANGLE_STRIP);
315	glNormal3f(0.0f, 1.0f, 0.0f);
316	glTexCoord2f(1.0f, 0.0f);
317	glVertex3f(-OB, 0.0f, +OB);
318	glTexCoord2f(1.0f-TO, 1.0f);
319	glVertex3f(-IB, 0.0f, +IB);
320	glTexCoord2f(1.0f-TO, 0.0f);
321	glVertex3f(-OB, 0.0f, +IB);
322	glTexCoord2f(TO, 1.0f);
323	glVertex3f(-IB, 0.0f, -IB);
324	glTexCoord2f(TO, 0.0f);
325	glVertex3f(-OB, 0.0f, -IB);
326	glTexCoord2f(0.0f, 0.0f);
327	glVertex3f(-OB, 0.0f, -OB);
328	glEnd();
329	glBegin(GL_TRIANGLE_STRIP);
330	glNormal3f(-1.0f, 0.0f, 0.0f);
331	glTexCoord2f(1.0f, 1.0f);
332	glVertex3f(-OB, 0.0f, +OB);
333	glTexCoord2f(0.0f, 1.0f);
334	glVertex3f(-OB, 0.0f, -OB);
335	glTexCoord2f(1.0f, 0.0f);
336	glVertex3d(-OB,  -DP, +OB);
337	glTexCoord2f(0.0f, 0.0f);
338	glVertex3d(-OB,  -DP, -OB);
339	glEnd();
340	//
341	// Right
342	//
343	glBegin(GL_TRIANGLE_STRIP);
344	glNormal3f(0.0f, 1.0f, 0.0f);
345	glTexCoord2f(0.0f, 0.0f);
346	glVertex3f(+OB, 0.0f, +OB);
347	glTexCoord2f(TO, 0.0f);
348	glVertex3f(+OB, 0.0f, +IB);
349	glTexCoord2f(TO, 1.0f);
350	glVertex3f(+IB, 0.0f, +IB);
351	glTexCoord2f(1.0f-TO, 0.0f);
352	glVertex3f(+OB, 0.0f, -IB);
353	glTexCoord2f(1.0f-TO, 1.0f);
354	glVertex3f(+IB, 0.0f, -IB);
355	glTexCoord2f(1.0f, 0.0f);
356	glVertex3f(+OB, 0.0f, -OB);
357	glEnd();
358	glBegin(GL_TRIANGLE_STRIP);
359	glNormal3f(1.0f, 0.0f, 0.0f);
360	glTexCoord2f(0.0f, 1.0f);
361	glVertex3f(+OB, 0.0f, +OB);
362	glTexCoord2f(0.0f, 0.0f);
363	glVertex3d(+OB,  -DP, +OB);
364	glTexCoord2f(1.0f, 1.0f);
365	glVertex3f(+OB, 0.0f, -OB);
366	glTexCoord2f(1.0f, 0.0f);
367	glVertex3d(+OB,  -DP, -OB);
368	glEnd();
369
370	glPopAttrib();
371}
372
373/* Draws the co-ordinates around the edge of the board */
374- (void) drawCoords
375{
376	glPushAttrib(GL_LIGHTING | GL_TEXTURE_BIT | GL_ENABLE_BIT);
377
378	glEnable(GL_TEXTURE_2D);
379	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
380	glEnable(GL_BLEND);
381	glDisable(GL_DEPTH_TEST);
382	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
383	glDisable(GL_LIGHTING);
384
385	const float kSize	= 3.5f;
386	const float kNHOff	= 1.25f;
387	const float kNVOff	= 3.25f;
388	const float kLHOff	= 3.25f;
389	const float kLVOff	= 1.00f;
390
391	/* Draw the numbers, always on the left and upright, no matter
392	   which color we're playing
393	*/
394	glColor4f(1.0f, 1.0f, 1.0f, fLabelIntensity);
395	for (int rows = 0; rows < 8; rows++)  {
396 		glBindTexture(GL_TEXTURE_2D, fNumberTextures[rows]);
397
398		float	t,l,b,r;
399
400		if ([self facingWhite]) {
401			l = -(40.0f + kNHOff + kSize);
402			r = -(40.0f + kNHOff);
403			t = 40.0f - kNVOff - rows*10.0f - kSize;
404			b = 40.0f - kNVOff - rows*10.0f;
405		} else {
406			r = -(40.0f + kNHOff + kSize);
407			l = -(40.0f + kNHOff);
408			b = 40.0f - kNVOff - rows*10.0f - kSize;
409			t = 40.0f - kNVOff - rows*10.0f;
410		}
411		glBegin(GL_QUADS);
412		glTexCoord2f(0.0f, 0.0f);
413		glVertex3f(l, 0.0f, b);
414		glTexCoord2f(1.0f, 0.0f);
415		glVertex3f(r, 0.0f, b);
416		glTexCoord2f(1.0f, 1.0f);
417		glVertex3f(r, 0.0f, t);
418		glTexCoord2f(0.0f, 1.0f);
419		glVertex3f(l, 0.0f, t);
420		glEnd();
421	}
422
423	/* Draw the letters */
424	for (int cols = 0; cols < 8; cols++) {
425		glBindTexture(GL_TEXTURE_2D, fLetterTextures[cols]);
426
427		float	t,l,b,r;
428
429		if ([self facingWhite]) {
430			t = 40.0f + kLVOff;
431			b = 40.0f + kLVOff + kSize;
432			l = cols*10.f + kLHOff - 40.0f;
433			r = cols*10.f + kLHOff - 40.0f + kSize;
434		} else {
435			t = -(40.0f + kLVOff);
436			b = -(40.0f + kLVOff + kSize);
437			r = cols*10.f + kLHOff - 40.0f;
438			l = cols*10.f + kLHOff - 40.0f + kSize;
439		}
440		glBegin(GL_QUADS);
441		glTexCoord2f(0.0f, 0.0f);
442		glVertex3f(l, 0.0f, b);
443		glTexCoord2f(1.0f, 0.0f);
444		glVertex3f(r, 0.0f, b);
445		glTexCoord2f(1.0f, 1.0f);
446		glVertex3f(r, 0.0f, t);
447		glTexCoord2f(0.0f, 1.0f);
448		glVertex3f(l, 0.0f, t);
449		glEnd();
450	}
451
452	glPopAttrib();
453}
454
455- (void) setupPieceDrawing:(BOOL)white reflect:(BOOL)reflection alpha:(float)alpha
456{
457	glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
458	glEnable(GL_BLEND);
459	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
460	if (reflection)
461		alpha *= fBoardReflectivity;
462	[fPieceDrawStyle[!white] startStyle:alpha];
463	glDepthMask(alpha > 0.5f);
464}
465
466- (void) endPieceDrawing
467{
468	glPopAttrib();
469}
470
471/* Draws a single piece */
472- (void) simplyDrawPiece:(MBCPiece)piece at:(MBCPosition)pos scale:(float)scale
473{
474	bool		wkr 	= false; /* white knight rotate flag */
475	int 		color	= Color(piece) != 0;
476	piece				= Piece(piece);
477
478	if (!color) 			/* white */
479		if (piece == KNIGHT)	/* white knight */
480			wkr = true;		/* white knight */
481
482	glPushMatrix();
483	glTranslatef(pos[0], pos[1], pos[2]);
484	if (wkr)		/* is white knight */
485		glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
486	glScalef(scale, scale, scale);
487	glCallList(piece);
488	glPopMatrix();
489
490	fLastPieceDrawn	= piece;
491}
492
493- (void) drawPiece:(MBCPiece)piece at:(MBCPosition)pos scale:(float)scale reflect:(BOOL)reflection alpha:(float)alpha
494{
495	[self setupPieceDrawing:!Color(piece) reflect:reflection alpha:alpha];
496	[self simplyDrawPiece:piece at:pos scale:scale];
497	[self endPieceDrawing];
498}
499
500- (void) drawPiece:(MBCPiece)piece at:(MBCPosition)pos reflect:(BOOL)reflection alpha:(float)alpha
501{
502	[self setupPieceDrawing:!Color(piece) reflect:reflection alpha:alpha];
503	[self simplyDrawPiece:piece at:pos scale:1.0f];
504	[self endPieceDrawing];
505}
506
507- (void) drawPiece:(MBCPiece)piece at:(MBCPosition)pos reflect:(BOOL)reflection
508{
509	[self setupPieceDrawing:!Color(piece) reflect:reflection alpha:1.0f];
510	[self simplyDrawPiece:piece at:pos scale:1.0f];
511	[self endPieceDrawing];
512}
513
514- (void) drawPiece:(MBCPiece)piece at:(MBCPosition)pos scale:(float)scale
515{
516	[self setupPieceDrawing:!Color(piece) reflect:NO alpha:1.0f];
517	[self simplyDrawPiece:piece at:pos scale:scale];
518	[self endPieceDrawing];
519}
520
521/* Draws the pieces */
522- (void) drawPieces:(BOOL)reflection
523{
524	for (MBCSquare square = 0; square<64; ++square) {
525		MBCPiece  piece = fInAnimation
526			? [fBoard oldContents:square]
527			: [fBoard curContents:square];
528		if (fSelectedPiece && square == fSelectedSquare)
529				continue;	// Skip original position of selected piece
530		if (piece) {
531			const MBCPosition pos = [self squareToPosition:square];
532			float dist			  =
533				fSelectedPiece && (!fInAnimation || square == fSelectedDest)
534				? fSelectedPos.FlatDistance(pos)
535				: 100.0f;
536			const float	kProximity = 5.0f;
537			if (dist < kProximity)
538				[self drawPiece:piece at:pos reflect:reflection
539					  alpha:pow(dist/kProximity, 4.0)];
540			else
541				[self drawPiece:piece at:pos reflect:reflection];
542		}
543	}
544}
545
546
547/* Draw the selected piece (may be off grid) */
548- (void) drawSelectedPiece:(BOOL)reflection
549{
550	[self drawPiece:fSelectedPiece at:fSelectedPos reflect:reflection];
551}
552
553/* Draw the promotion piece (transparent) */
554- (void) drawPromotionPiece
555{
556	MBCPiece 	piece = EMPTY;
557	MBCPosition pos;
558
559	fPromotionSide = kNeitherSide;
560
561	if (fSide == kWhiteSide || fSide == kBothSides)
562		if ([fBoard canPromote:kWhiteSide]) {
563			piece			=	[fBoard defaultPromotion:YES];
564			fPromotionSide	=	kWhiteSide;
565			pos[0]			= 	-kPromotionPieceX;
566			pos[1]			= 	  0.0f;
567			pos[2]			=   -kPromotionPieceZ;
568		}
569	if (fSide == kBlackSide || fSide == kBothSides)
570		if ([fBoard canPromote:kBlackSide]) {
571			piece			=	[fBoard defaultPromotion:NO];
572			fPromotionSide	= 	kBlackSide;
573			pos[0]			= 	kPromotionPieceX;
574			pos[1]			= 	  0.0f;
575			pos[2]			=   kPromotionPieceZ;
576		}
577	if (fPromotionSide == kNeitherSide)
578		return;
579
580	bool		wkr		= (fPromotionSide == kWhiteSide && piece == KNIGHT);
581
582	glPushAttrib(GL_ENABLE_BIT|GL_TEXTURE_BIT);
583
584	[fSelectedPieceDrawStyle startStyle:1.0f];
585
586	glEnable(GL_BLEND);
587	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
588
589	glPushMatrix();
590	glTranslatef(pos[0], pos[1], pos[2]);
591	if (wkr)			/* is white knight */
592		glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
593	glCallList(Piece(piece));
594	glPopMatrix();
595
596	glPopAttrib();
597}
598
599- (void) placeLights
600{
601	const float kDiffuse = 0.75;
602	GLfloat l_diffuse[4] = { kDiffuse, kDiffuse, kDiffuse, 1.0 };
603	GLfloat l_ambient[4] = { fAmbient, fAmbient, fAmbient, 0.0 };
604
605	glEnable(GL_LIGHT0);
606	glLightfv(GL_LIGHT0, GL_DIFFUSE, l_diffuse);
607	glLightfv(GL_LIGHT0, GL_AMBIENT, l_ambient);
608	glLightfv(GL_LIGHT0, GL_SPECULAR, l_diffuse);
609	glLightfv(GL_LIGHT0, GL_POSITION, fLightPos);
610
611	if (fPickedSquare != kInvalidSquare) {
612		GLfloat spot_color[4]		= { 1.0, 1.0, 1.0, 1.0 };
613		GLfloat spot_pos[4] 		= { 0.0, 100.0, 0.0, 1.0};
614		GLfloat spot_direction[3]   = { 0.0, -1.0, 0.0 };
615
616		MBCPosition pickedPos = [self squareToPosition:fPickedSquare];
617
618		spot_pos[0]	= pickedPos[0];
619		spot_pos[2] = pickedPos[2];
620
621		glEnable(GL_LIGHT1);
622		glLightfv(GL_LIGHT1, GL_DIFFUSE, spot_color);
623		glLightfv(GL_LIGHT1, GL_SPECULAR, spot_color);
624		glLightfv(GL_LIGHT1, GL_POSITION, spot_pos);
625		glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, spot_direction);
626		glLighti(GL_LIGHT1, GL_SPOT_EXPONENT, 100);
627		glLighti(GL_LIGHT1, GL_SPOT_CUTOFF, 5);
628		glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 0.0f);
629		glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0001f);
630	} else
631		glDisable(GL_LIGHT1);
632}
633
634MBCPieceCode gInHandOrder[] = {PAWN, BISHOP, KNIGHT, ROOK, QUEEN};
635
636- (void) drawPiecesInHand
637{
638	const float kLabelX 	=  42.0f;
639	const float kLabelSize	=  3.0f;
640	const float	kLabelLeft	=  kLabelX;
641	const float kLabelRight	=  kLabelX+kLabelSize;
642	const float kSpacing	=  kInHandPieceSize;
643	const float kLabelZ		=  6.0f;
644	const float kPieceX		=  kInHandPieceX;
645	const float kPieceZ		=  kInHandPieceZOffset+kInHandPieceSize/2.0f;
646	const float kScale		=  0.95f;
647	const bool  kFlip		=  fAzimuth < 90.0f || fAzimuth >= 270.0f;
648	const float	kTexLeft	=  kFlip ? 1.0f : 0.0f;
649	const float kTexRight	=  1.0f-kTexLeft;
650	const float kTexBottom	=  kFlip ? 1.0f : 0.0f;
651	const float kTexTop		=  1.0f-kTexBottom;
652
653	glPushAttrib(GL_LIGHTING | GL_ENABLE_BIT | GL_TEXTURE_BIT);
654
655	glEnable(GL_TEXTURE_2D);
656	glEnable(GL_BLEND);
657	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
658	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
659	glDisable(GL_LIGHTING);
660	glDisable(GL_DEPTH_TEST);
661
662	/* Draw the numbers */
663	glColor4f(1.0f, 1.0f, 1.0f, fLabelIntensity);
664
665	for (int p = 0; p<5; ++p) {
666		MBCPiece piece = White(gInHandOrder[p]);
667		int numInHand = fInAnimation
668			? [fBoard oldInHand:piece]
669			: [fBoard curInHand:piece];
670		numInHand -= (piece+kInHandSquare == fSelectedSquare);
671		if (numInHand > 1) {
672			glBindTexture(GL_TEXTURE_2D, fNumberTextures[min(numInHand,8)-1]);
673
674			float z	= p * kSpacing;
675
676			glBegin(GL_QUADS);
677			glTexCoord2f(kTexLeft, kTexBottom);
678			glVertex3f(kLabelLeft, 0.0f,  kLabelZ+z+kLabelSize);
679			glTexCoord2f(kTexRight, kTexBottom);
680			glVertex3f(kLabelRight, 0.0f, kLabelZ+z+kLabelSize);
681			glTexCoord2f(kTexRight, kTexTop);
682			glVertex3f(kLabelRight, 0.0f, kLabelZ+z);
683			glTexCoord2f(kTexLeft, kTexTop);
684			glVertex3f(kLabelLeft, 0.0f,  kLabelZ+z);
685			glEnd();
686		}
687	}
688
689	for (int p = 0; p<5; ++p) {
690		MBCPiece piece = Black(gInHandOrder[p]);
691		int numInHand = fInAnimation
692			? [fBoard oldInHand:piece]
693			: [fBoard curInHand:piece];
694		numInHand -= (piece+kInHandSquare == fSelectedSquare);
695		if (numInHand > 1) {
696			glBindTexture(GL_TEXTURE_2D, fNumberTextures[min(numInHand,8)-1]);
697
698			float z	= p * kSpacing;
699
700			glBegin(GL_QUADS);
701			glTexCoord2f(kTexLeft, kTexBottom);
702			glVertex3f(kLabelLeft, 0.0f,  -kLabelZ-z);
703			glTexCoord2f(kTexRight, kTexBottom);
704			glVertex3f(kLabelRight, 0.0f, -kLabelZ-z);
705			glTexCoord2f(kTexRight, kTexTop);
706			glVertex3f(kLabelRight, 0.0f, -kLabelZ-z-kLabelSize);
707			glTexCoord2f(kTexLeft, kTexTop);
708			glVertex3f(kLabelLeft, 0.0f,  -kLabelZ-z-kLabelSize);
709			glEnd();
710		}
711	}
712
713	glDisable(GL_BLEND);
714
715	glPopAttrib();
716
717	[self placeLights];
718
719	for (int p = 0; p<5; ++p) {
720		MBCPiece piece = White(gInHandOrder[p]);
721		int numInHand = fInAnimation
722			? [fBoard oldInHand:piece]
723			: [fBoard curInHand:piece];
724		numInHand -= (piece+kInHandSquare == fSelectedSquare);
725		if (numInHand) {
726			MBCPosition pos = {{kPieceX, 0.0f,  kPieceZ}};
727			pos[2]		   += p*kSpacing;
728
729			[self drawPiece:piece at:pos scale:kScale];
730		}
731	}
732
733	for (int p = 0; p<5; ++p) {
734		MBCPiece piece = Black(gInHandOrder[p]);
735		int numInHand = fInAnimation
736			? [fBoard oldInHand:piece]
737			: [fBoard curInHand:piece];
738		numInHand -= (piece+kInHandSquare == fSelectedSquare);
739		if (numInHand) {
740			MBCPosition pos = {{kPieceX, 0.0f,  -kPieceZ}};
741			pos[2]		   -= p*kSpacing;
742
743			[self drawPiece:piece at:pos scale:kScale];
744		}
745	}
746	glPopMatrix();
747}
748
749- (void) drawArrowFrom:(MBCPosition)fromPos to:(MBCPosition)toPos width:(float)w
750{
751	glPushAttrib(GL_ENABLE_BIT);	/* Save states */
752	glDisable(GL_DEPTH_TEST);
753	glDisable(GL_TEXTURE_2D);
754	glDisable(GL_LIGHTING);
755	glEnable(GL_BLEND);
756   	glDisable(GL_CULL_FACE);	    /* Too lazy to figure out winding */
757	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
758
759	const float	kStemW  = w;
760	const float	kPointW	= 2.00f*w;
761	const float	kPointH	= 1.50f*w;
762	const float	kHeight	= 0.01f;
763
764	const float len				=
765		hypot(toPos[0]-fromPos[0], toPos[2]-fromPos[2]);
766	const float	alpha			=
767		atan2(toPos[2]-fromPos[2], toPos[0]-fromPos[0]);
768	const float sinAlpha		= sin(alpha);
769	const float cosAlpha		= cos(alpha);
770
771	MBCPosition p1	=	fromPos;
772	p1.pos[0]	   -= 	kStemW*sinAlpha;
773	p1.pos[1]		= 	kHeight;
774	p1.pos[2]	   += 	kStemW*cosAlpha;
775	MBCPosition p2	=	fromPos;
776	p2.pos[0]	   += 	kStemW*sinAlpha;
777	p2.pos[1]		= 	kHeight;
778	p2.pos[2]	   -= 	kStemW*cosAlpha;
779	MBCPosition p3	=	p1;
780	p3.pos[0]	   += 	(len-kPointH)*cosAlpha;
781	p3.pos[2]	   += 	(len-kPointH)*sinAlpha;
782	MBCPosition p4	=	p2;
783	p4.pos[0]	   += 	(len-kPointH)*cosAlpha;
784	p4.pos[2]	   += 	(len-kPointH)*sinAlpha;
785	MBCPosition p5	=	p3;
786	p5.pos[0]	   -= 	(kPointW-kStemW)*sinAlpha;
787	p5.pos[2]	   += 	(kPointW-kStemW)*cosAlpha;
788	MBCPosition p6	=	p4;
789	p6.pos[0]	   += 	(kPointW-kStemW)*sinAlpha;
790	p6.pos[2]	   -= 	(kPointW-kStemW)*cosAlpha;
791	MBCPosition p7	=	toPos;
792	p7.pos[1]		= 	kHeight;
793
794	glBegin(GL_TRIANGLES);
795	glVertex3fv(p1);
796	glVertex3fv(p2);
797	glVertex3fv(p4);
798	glVertex3fv(p4);
799	glVertex3fv(p3);
800	glVertex3fv(p1);
801	glVertex3fv(p5);
802	glVertex3fv(p6);
803	glVertex3fv(p7);
804	glEnd();
805
806	glPopAttrib();
807}
808
809- (void) drawMove:(MBCMove *)move asHint:(BOOL)hint
810{
811	if (!move)
812		return;
813
814	MBCPosition	fromPos	= [self squareToPosition: move->fFromSquare];
815	MBCPosition	toPos	= [self squareToPosition: move->fToSquare];
816
817	if (hint)
818		glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
819	else
820		glColor4f(0.0f, 0.0f, 1.0f, 0.5f);
821
822	[self drawArrowFrom:fromPos to:toPos width:2.0f];
823}
824
825- (void) drawManipulator
826{
827	//
828	// Save normal projection and superimpose an Ortho projection
829	//
830	glPushMatrix();
831	glLoadIdentity();
832	glMatrixMode(GL_PROJECTION);
833	glPushMatrix();
834	glLoadIdentity();
835	NSRect b = [self bounds];
836	gluOrtho2D(NSMinX(b), NSMaxX(b), NSMinY(b), NSMaxY(b));
837	glMatrixMode(GL_MODELVIEW);
838    glRotatef(-90.0, 1.0, 0.0, 0.0);
839	//
840	// Draw navigation aid
841	//
842	bool	    horizontal		=
843		fabs(fCurMouse.x-fOrigMouse.x) > fabs(fCurMouse.y-fOrigMouse.y);
844	const float	kScale			= 1.0f;
845	const float	kCircleSize		= 10.0f*kScale;
846	const float	kArrowClearance	= 15.0f*kScale;
847	const float	kArrowLength	= 30.0f*kScale;
848	const float kArrowWidth		= 10.0f*kScale;
849	const float	kThreshold		= 10.0f*kScale;
850	const float kWellSize		= 55.0f*kScale;
851	const float kWellRound		= 20.0f*kScale;
852
853	GLfloat on_color[4] 		= {1.0f, 1.0f, 1.0f, 1.0f};
854	GLfloat off_color[4] 		= {1.0f, 1.0f, 1.0f, 0.4f};
855	GLfloat	well_color[4]		= {0.5f, 0.5f, 0.5f, 0.6f};
856	//
857	// Well & Circle
858	//
859	glPushAttrib(GL_ENABLE_BIT);	/* Save states */
860	glDisable(GL_DEPTH_TEST);
861	glDisable(GL_TEXTURE_2D);
862	glDisable(GL_LIGHTING);
863	glEnable(GL_BLEND);
864   	glDisable(GL_CULL_FACE);	    /* Too lazy to figure out winding */
865	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
866
867	GLUquadricObj * q 		= gluNewQuadric();
868
869	glPushMatrix();
870    glRotatef(90.0, 1.0, 0.0, 0.0);
871	glTranslatef(fOrigMouse.x, fOrigMouse.y, 0.01f);
872
873	glColor4fv(well_color);
874	glBegin(GL_QUADS);
875	glVertex3f(-kWellSize+kWellRound, -kWellSize, 0.0f);
876	glVertex3f( kWellSize-kWellRound, -kWellSize, 0.0f);
877	glVertex3f( kWellSize-kWellRound, -kWellSize+kWellRound, 0.0f);
878	glVertex3f(-kWellSize+kWellRound, -kWellSize+kWellRound, 0.0f);
879	glVertex3f(-kWellSize, -kWellSize+kWellRound, 0.0f);
880	glVertex3f( kWellSize, -kWellSize+kWellRound, 0.0f);
881	glVertex3f( kWellSize,  kWellSize-kWellRound, 0.0f);
882	glVertex3f(-kWellSize,  kWellSize-kWellRound, 0.0f);
883	glVertex3f(-kWellSize+kWellRound, kWellSize-kWellRound, 0.0f);
884	glVertex3f( kWellSize-kWellRound, kWellSize-kWellRound, 0.0f);
885	glVertex3f( kWellSize-kWellRound, kWellSize, 0.0f);
886	glVertex3f(-kWellSize+kWellRound, kWellSize, 0.0f);
887	glEnd();
888	glTranslatef(-kWellSize+kWellRound, -kWellSize+kWellRound, 0.0f);
889	gluPartialDisk(q, 0.0, kWellRound, 10, 1, 180.0, 90.0);
890	glTranslatef(2.0*(kWellSize-kWellRound), 0.0f, 0.0f);
891	gluPartialDisk(q, 0.0, kWellRound, 10, 1,  90.0, 90.0);
892	glTranslatef(0.0, 2.0*(kWellSize-kWellRound), 0.0f);
893	gluPartialDisk(q, 0.0, kWellRound, 10, 1,   0.0, 90.0);
894	glTranslatef(-2.0*(kWellSize-kWellRound), 0.0f, 0.0f);
895	gluPartialDisk(q, 0.0, kWellRound, 10, 1, 270.0, 90.0);
896	glTranslatef( kWellSize-kWellRound, -kWellSize+kWellRound, 0.0f);
897
898	glColor4fv(fabs(fCurMouse.x-fOrigMouse.x)<kThreshold
899			&& fabs(fCurMouse.y-fOrigMouse.y)<kThreshold
900			   ? on_color : off_color);
901	gluDisk(q, 0.0, kCircleSize, 40, 1);
902	glPopMatrix();
903	gluDeleteQuadric(q);
904
905	MBCPosition	fromPos, toPos;
906
907	//
908	// Up
909	//
910	fromPos[0] = fOrigMouse.x;
911	fromPos[1] = 0;
912	fromPos[2] = fOrigMouse.y+kArrowClearance;
913	toPos	   = fromPos;
914	toPos[2]  += kArrowLength;
915	glColor4fv((!horizontal && (fCurMouse.y > fOrigMouse.y+kThreshold))
916			   ? on_color : off_color);
917	[self drawArrowFrom:fromPos to:toPos width:kArrowWidth];
918
919	//
920	// Down
921	//
922	fromPos[0] = fOrigMouse.x;
923	fromPos[1] = 0;
924	fromPos[2] = fOrigMouse.y-kArrowClearance;
925	toPos	   = fromPos;
926	toPos[2]  -= kArrowLength;
927	glColor4fv((!horizontal && (fCurMouse.y < fOrigMouse.y-kThreshold))
928			   ? on_color : off_color);
929	[self drawArrowFrom:fromPos to:toPos width:kArrowWidth];
930
931	//
932	// Right
933	//
934	fromPos[0] = fOrigMouse.x+kArrowClearance;
935	fromPos[1] = 0;
936	fromPos[2] = fOrigMouse.y;
937	toPos	   = fromPos;
938	toPos[0]  += kArrowLength;
939	glColor4fv((horizontal && (fCurMouse.x > fOrigMouse.x+kThreshold))
940			   ? on_color : off_color);
941	[self drawArrowFrom:fromPos to:toPos width:kArrowWidth];
942
943	//
944	// Left
945	//
946	fromPos[0] = fOrigMouse.x-kArrowClearance;
947	fromPos[1] = 0;
948	fromPos[2] = fOrigMouse.y;
949	toPos	   = fromPos;
950	toPos[0]  -= kArrowLength;
951	glColor4fv((horizontal && (fCurMouse.x < fOrigMouse.x-kThreshold))
952			   ? on_color : off_color);
953	[self drawArrowFrom:fromPos to:toPos width:kArrowWidth];
954
955	glPopAttrib();
956	//
957	// Restore projection
958	//
959	glPopMatrix();
960	glMatrixMode(GL_PROJECTION);
961	glPopMatrix();
962	glMatrixMode(GL_MODELVIEW);
963}
964
965- (void) makeBoardSolid
966{
967	//
968	// If we're in a transparent window, we have to make sure that the
969	// board itself always remains opaque, no matter what blending we've
970	// done with it
971	//
972	const float IB = kBoardRadius; 		// Inside border
973	const float OB = IB+kBorderWidth; 	// Outside border
974	const float DP =  5.0f;				// Depth
975
976	glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT);
977	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
978	glDisable(GL_BLEND);
979	glDisable(GL_LIGHTING);
980   	glDisable(GL_CULL_FACE);	    /* Too lazy to figure out winding */
981	glDisable(GL_DEPTH_TEST);
982	glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
983	glBegin(GL_QUADS);
984	glVertex3f(-OB, 0.0f,  OB);
985	glVertex3f( OB, 0.0f,  OB);
986	glVertex3f( OB, 0.0f, -OB);
987	glVertex3f(-OB, 0.0f, -OB);
988	glEnd();
989	glBegin(GL_QUAD_STRIP);
990	glVertex3f(-OB,  -DP,  OB);
991	glVertex3f(-OB, 0.0f,  OB);
992	glVertex3f( OB,  -DP,  OB);
993	glVertex3f( OB, 0.0f,  OB);
994	glVertex3f( OB,  -DP, -OB);
995	glVertex3f( OB, 0.0f, -OB);
996	glVertex3f(-OB,  -DP, -OB);
997	glVertex3f(-OB, 0.0f, -OB);
998	glEnd();
999	glPopAttrib();
1000}
1001
1002- (void) drawGloss
1003{
1004	//
1005	// Save normal projection and superimpose an Ortho projection
1006	//
1007	glPushMatrix();
1008	glLoadIdentity();
1009	glMatrixMode(GL_PROJECTION);
1010	glPushMatrix();
1011	glLoadIdentity();
1012	NSRect b = [self bounds];
1013	gluOrtho2D(NSMinX(b), NSMaxX(b), NSMinY(b), NSMaxY(b));
1014	glMatrixMode(GL_MODELVIEW);
1015
1016    glPushAttrib(GL_ENABLE_BIT);	/* Save states */
1017	glDisable(GL_DEPTH_TEST);
1018	glDisable(GL_TEXTURE_2D);
1019	glDisable(GL_LIGHTING);
1020	glDisable(GL_CULL_FACE);
1021	glDisable(GL_BLEND);
1022
1023    GLfloat kBaseColor[3] = {0.25f, 0.25f, 0.25f};
1024    GLfloat kTopColor[3]  = {0.6f, 0.6f, 0.6f};
1025    GLfloat kMidColor[3]  = {0.3f, 0.3f, 0.3f};
1026    const int kSegments   = 32;
1027    GLfloat kAspectRatio  = b.size.width/b.size.height;
1028    GLfloat kMidX         = b.size.width*0.5f;
1029    GLfloat kMidY         = b.size.height*3.0f*kAspectRatio;
1030    GLfloat kSecY         = b.size.height*0.55f;
1031    GLfloat kRadius       = hypotf(kMidX, kMidY-kSecY);
1032    GLfloat kRadius2      = kRadius*kRadius;
1033
1034    glColor3fv(kBaseColor);
1035    glBegin(GL_QUADS);
1036    glVertex3f(NSMinX(b), NSMinY(b), 0.0f);
1037    glVertex3f(NSMaxX(b), NSMinY(b), 0.0f);
1038    glVertex3f(NSMaxX(b), NSMaxY(b), 0.0f);
1039    glVertex3f(NSMinX(b), NSMaxY(b), 0.0f);
1040    glEnd();
1041
1042    glBegin(GL_TRIANGLE_STRIP);
1043    for (int i=0; i<=kSegments; ++i) {
1044        GLfloat x   =   b.origin.x+b.size.width*i/kSegments;
1045        GLfloat dx  =   kMidX-x;
1046        GLfloat y0  =   kMidY-sqrtf(kRadius2-dx*dx);
1047        GLfloat y1  =   b.origin.y+b.size.height;
1048
1049        glColor3fv(kMidColor);
1050        glVertex3f(x, y0, 0.0f);
1051        glColor3fv(kTopColor);
1052        glVertex3f(x, y1, 0.0f);
1053    }
1054    glEnd();
1055
1056	//
1057	// Restore projection
1058	//
1059    glPopAttrib();
1060	glPopMatrix();
1061	glMatrixMode(GL_PROJECTION);
1062	glPopMatrix();
1063	glMatrixMode(GL_MODELVIEW);
1064}
1065
1066- (void) update
1067{
1068	[super update];
1069	if (![[self openGLContext] view]) {
1070		//
1071		//     Fall back to a less memory hungry format.
1072		//
1073		[self pickPixelFormat:YES];
1074		fNeedPerspective = true;
1075	}
1076}
1077
1078/* Draw the scene for a game */
1079- (void) drawPosition
1080{
1081	if (![[self openGLContext] view]) {
1082		//
1083		//     Fall back to a less memory hungry format.
1084		//
1085		[self pickPixelFormat:YES];
1086		fNeedPerspective = true;
1087	}
1088
1089	if (fIsFloating) {
1090		[[NSColor clearColor] set];
1091		NSRectFill([self bounds]);
1092	}
1093
1094	if (fNeedStaticModels) {
1095		fNeedStaticModels = false;
1096		[self loadColors];
1097		[self generateModelLists];
1098	}
1099	if (fNeedPerspective)
1100		[self setupPerspective];
1101
1102	/* Clear the buffers */
1103	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1104	if (fBoardReflectivity)
1105		glClear(GL_STENCIL_BUFFER_BIT);
1106	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1107
1108    [self drawGloss];
1109
1110	/* Place lights */
1111	[self placeLights];
1112
1113	/* Draw the board */
1114   	[self drawBoard:NO];
1115
1116	/* Make a stencil of the floor if reflections are done */
1117	if (fBoardReflectivity) {
1118		/* Save the old scene attributes */
1119		glPushAttrib(GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT);
1120
1121		/* Disable stuff */
1122		glDisable(GL_DEPTH_TEST);
1123		glDisable(GL_LIGHTING);
1124		glDisable(GL_BLEND);
1125
1126		/* Don't draw to the screen or the depth buffer at this moment */
1127		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1128		glDepthMask(GL_FALSE);
1129
1130		/* Write to the stencil buffer */
1131		glEnable(GL_STENCIL_TEST);
1132		glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
1133		glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
1134
1135		glBegin(GL_QUADS);
1136		glVertex3f(-40.0f, 0.0f,  40.0f);
1137		glVertex3f( 40.0f, 0.0f,  40.0f);
1138		glVertex3f( 40.0f, 0.0f, -40.0f);
1139		glVertex3f(-40.0f, 0.0f, -40.0f);
1140		glEnd();
1141
1142		//
1143		// Re-enable writing to the depth buffer and to the color channels
1144		// but NOT to the alpha channel in case we have a translucent window
1145		//
1146		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
1147		glDepthMask(GL_TRUE);
1148
1149		/* Draw only if stencil is set to 1 */
1150		glStencilFunc(GL_EQUAL, 1, 0xffffffff);
1151		glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
1152
1153		/* draw the reflected pieces */
1154
1155		/* Reflect in floor */
1156		glPushMatrix(); {
1157			glScalef(1.0f, -1.0f, 1.0f);
1158
1159			glEnable(GL_LIGHTING);
1160			[self placeLights];
1161
1162			glCullFace(GL_FRONT); {
1163			    [self drawPieces:YES];
1164
1165			    if (fSelectedPiece)
1166					[self drawSelectedPiece:YES];
1167			} glCullFace(GL_BACK);
1168		} glPopMatrix();
1169
1170		/* Restore the scene attributes */
1171		glPopAttrib();
1172
1173		/* Now blend board back into the reflections */
1174		[self drawBoard:YES];
1175	}
1176
1177	/* Draw the co-ordinates [1-8] [a-h] */
1178	[self drawCoords];
1179
1180	/* Draw hint and last move */
1181	[self drawMove:fHintMove asHint:YES];
1182	[self drawMove:fLastMove asHint:NO];
1183
1184	glDisable(GL_BLEND);
1185
1186	/* Draw the pieces */
1187    if (fVariant == kVarCrazyhouse)
1188		[self drawPiecesInHand];
1189	[self drawPieces:NO];
1190
1191	if (fSelectedPiece)
1192		[self drawSelectedPiece:NO];
1193
1194	[self drawPromotionPiece];
1195
1196#if 0
1197	//
1198	// Some graphics cards seem to mess up the lighting when the knight
1199	// or king model were the last ones drawn
1200	//
1201	if (fLastPieceDrawn == KING || fLastPieceDrawn == KNIGHT) {
1202		MBCPosition pos = {{0.0f, 0.0f, 0.0f}};
1203		[self drawPiece:PAWN at:pos reflect:NO alpha:0.0f];
1204	}
1205#endif
1206
1207	if (fInBoardManipulation)
1208		[self drawManipulator];
1209
1210    [self makeBoardSolid];
1211
1212	//
1213	// Work around OpenGL driver issue
1214	//
1215#ifdef __LP64__
1216	glFlush();
1217#endif
1218
1219	[[self openGLContext] flushBuffer];
1220}
1221
1222@end
1223
1224// Local Variables:
1225// mode:ObjC
1226// End:
1227