MBCBoardViewDraw.mm   [plain text]


/*
	File:		MBCBoardViewDraw.mm
	Contains:	Draw chess board
	Version:	1.0
	Copyright:	© 2002-2006 by Apple Computer, Inc., all rights reserved.
	
	Derived from glChess, Copyright © 2002 Robert Ancell and Michael Duelli
	Permission granted to Apple to relicense under the following terms:

	File Ownership:

		DRI:				Matthias Neeracher    x43683

	Writers:

		(MN)	Matthias Neeracher

	Change History (most recent first):

		$Log: MBCBoardViewDraw.mm,v $
		Revision 1.42  2008/10/24 01:17:14  neerache
		<rdar://problem/5973744> Chess Needs To Move from SGI Format Images to PNG or JPEG
		
		Revision 1.41  2007/01/16 21:23:47  neerache
		Adapt to changed HiDPI code for NSOpenGLView
		
		Revision 1.40  2006/10/09 21:05:01  neerache
		Correct HiDPI problems <rdar://problem/4412218>
		
		Revision 1.39  2006/07/27 22:00:02  neerache
		Work around OpenGL driver issue for WWDC <rdar://problem/4655889>
		
		Revision 1.38  2006/03/28 02:53:00  neerache
		Fix incorrect type
		
		Revision 1.37  2004/12/20 09:39:29  neerache
		Implement self test (RADAR 3590419 / Feature 8905)
		
		Revision 1.36  2004/08/16 07:50:23  neerache
		Hilight picked piece
		
		Revision 1.35  2004/07/10 04:53:29  neerache
		Tweak visuals
		
		Revision 1.34  2003/11/06 23:30:51  neerache
		Adjust wording as suggested by Joyce Chow
		
		Revision 1.33  2003/10/29 22:39:31  neerache
		Add tools & clean up copyright references for release
		
		Revision 1.32  2003/08/06 00:13:43  neerache
		Respect label intensities again
		
		Revision 1.31  2003/08/01 23:53:19  neerache
		Get rid of erroneous use of GL_SRC_COLOR (RADAR 3343477)
		
		Revision 1.30  2003/07/14 23:21:49  neerache
		Move promotion defaults into MBCBoard
		
		Revision 1.29  2003/07/07 23:50:42  neerache
		Work around a graphics bug
		
		Revision 1.28  2003/07/07 09:16:42  neerache
		Textured windows are too slow for low end machines, disable
		
		Revision 1.27  2003/07/07 08:47:53  neerache
		Switch to textured main window
		
		Revision 1.26  2003/06/18 21:55:17  neerache
		More (mostly unsuccessful) tweaking of floating windows
		
		Revision 1.25  2003/06/16 02:18:03  neerache
		Implement floating board
		
		Revision 1.24  2003/06/15 21:13:09  neerache
		Adjust lights, fix animation, work on other drawing issues
		
		Revision 1.23  2003/06/05 08:31:26  neerache
		Added Tuner
		
		Revision 1.22  2003/06/05 00:14:37  neerache
		Reduce excessive threshold
		
		Revision 1.21  2003/06/04 23:14:05  neerache
		Neater manipulation widget; remove obsolete graphics options
		
		Revision 1.20  2003/06/04 09:25:47  neerache
		New and improved board manipulation metaphor
		
		Revision 1.19  2003/06/02 05:44:48  neerache
		Implement direct board manipulation
		
		Revision 1.18  2003/06/02 04:21:40  neerache
		Start implementing drawing styles for board elements
		
		Revision 1.17  2003/05/27 07:25:48  neerache
		Apply scaling and translation in proper order
		
		Revision 1.16  2003/05/24 20:31:23  neerache
		Lots of graphics improvements, espcially with specular light
		
		Revision 1.15  2003/05/23 03:22:16  neerache
		Add FPS computation
		
		Revision 1.14  2003/05/05 23:50:40  neerache
		Tweak appearance, add border, add animations
		
		Revision 1.13  2003/05/02 01:16:55  neerache
		Antialias squares, experiment with translucent board
		
		Revision 1.12  2003/04/28 22:19:29  neerache
		Eliminate drawBoardPlane; simplify background; move labels closer to board; vary square textures; properly rotate selected knights; reorder elements to be drawn
		
		Revision 1.11  2003/04/24 23:20:35  neeri
		Support pawn promotions
		
		Revision 1.10  2003/04/10 23:03:16  neeri
		Load positions
		
		Revision 1.9  2003/04/02 18:44:01  neeri
		Tweak perspective
		
		Revision 1.8  2003/03/28 01:31:07  neeri
		Support hints, last move
		
		Revision 1.7  2002/12/04 02:30:50  neeri
		Experiment (unsuccessfully so far) with ways to speed up piece movement
		
		Revision 1.6  2002/10/15 22:49:39  neeri
		Add support for texture styles
		
		Revision 1.5  2002/10/08 22:59:11  neeri
		Refine drawing, support flipped board
		
		Revision 1.4  2002/09/13 23:57:05  neeri
		Support for Crazyhouse display and mouse
		
		Revision 1.3  2002/09/12 17:46:46  neeri
		Introduce dual board representation, in-hand pieces
		
		Revision 1.2  2002/08/26 23:14:08  neeri
		Switched to Azimuth/Elevation model; fixed lighting issue manifesting with white knights
		
		Revision 1.1  2002/08/22 23:47:06  neeri
		Initial Checkin
		
*/

#import "MBCBoardViewDraw.h"

#import <math.h>
#import <OpenGL/glu.h>
#import <algorithm>
#import <sys/time.h>

using std::min;

@implementation MBCDrawStyle

- (id) init
{
	fTexture	= 0;

	return self;
}

- (id) initWithTexture:(GLuint)tex
{
	fTexture	= tex;
	fDiffuse	= 1.0f;
	fSpecular	= 0.2f;
	fShininess	= 5.0f;
	fAlpha		= 1.0f;

	return self;
}

- (void) unloadTexture
{
	if (fTexture)
		glDeleteTextures(1, &fTexture);
}

- (void) startStyle:(float)alpha
{
	GLfloat white_texture_color[4] 	= 
		{fDiffuse, fDiffuse, fDiffuse, fAlpha*alpha};
	GLfloat emission_color[4] 		= 
		{0.0f, 0.0f, 0.0f, fAlpha*alpha};
	GLfloat specular_color[4] 		= 
		{fSpecular, fSpecular, fSpecular, fAlpha*alpha};

	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, white_texture_color);
	glMaterialfv(GL_FRONT, GL_EMISSION, emission_color);
	glMaterialfv(GL_FRONT, GL_SPECULAR, specular_color);
	glMaterialf(GL_FRONT, GL_SHININESS, fShininess);
	glBindTexture(GL_TEXTURE_2D, fTexture);
}

@end

@implementation MBCBoardView ( Draw )

- (void) setupPerspective
{
	fIsFloating			= ![[self window] styleMask];
	if (!fIsFloating) {
		//
		// Regular window, draw background
		//
		const float kBrightness = 0.6f;
		glClearColor(kBrightness, kBrightness, kBrightness, 1.0);
	} else {
		//
		// Floating window, transparent background
		//
		GLint opaque = NO;
		[[self openGLContext] setValues:&opaque 
							  forParameter:NSOpenGLCPSurfaceOpacity];
		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	}

	/* Stuff you can't do without */
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glEnable(GL_NORMALIZE);

	/* Textures */
	glEnable(GL_TEXTURE_2D);

	/* The lighting */
	glEnable(GL_LIGHTING);
		
	glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
	glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
	glShadeModel(GL_SMOOTH);

	glDisable(GL_FOG);

	const float	kUserSpaceScale = 1.0f / [[self window] userSpaceScaleFactor];
	const float kDistance 		= 300.0f;
	const float kBoardSize 		= fVariant==kVarCrazyhouse ? 55.0f : 50.0f;
	const float kDeg2Rad  		= M_PI / 180.0f;
	const float kRad2Deg		= 180.0f / M_PI;
	const float kAngleOfView	= 2.0f * atan2(kBoardSize, kDistance) * kRad2Deg;

	NSRect bounds = [self bounds];
	glViewport(0, 0, (long)(kUserSpaceScale*bounds.size.width), (long)(kUserSpaceScale*bounds.size.height));

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

    gluPerspective(kAngleOfView, bounds.size.width / bounds.size.height, 
				   10.0, 1000.0); 

	glMatrixMode(GL_TEXTURE);
	glLoadIdentity();
	glScalef(1.0f, -1.0f, 0.0f);
	
	glMatrixMode(GL_MODELVIEW);

	float	cameraY = kDistance * sin(fElevation * kDeg2Rad);
	float   cameraXZ= kDistance * cos(fElevation * kDeg2Rad);
	float	cameraX = cameraXZ  * sin(fAzimuth * kDeg2Rad);
	float	cameraZ = cameraXZ  *-cos(fAzimuth * kDeg2Rad);

	gluLookAt(cameraX, cameraY, cameraZ,
			  0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

	fNeedPerspective	= false;
}

- (void) drawBoard:(BOOL)overReflection
{
	int x, y, color;

	//
	// We want variation in the squares, not psychedelic effects
	//
	srandom(1);

	glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_COLOR_BUFFER_BIT);

	//
	// Blend edges of squares
	//
	if (overReflection)
		glDisable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_LINE_SMOOTH);
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
	// glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, !overReflection);

	glNormal3f(0.0f, 1.0f, 0.0f);

	for (x = 0; x < 8; x++) {
		for (y = 0; y < 8; y++) {
			/* Get board piece color */
			color = (x % 2 == y % 2);
			
			[fBoardDrawStyle[color] 
							startStyle:overReflection 	
							? 1.0f-fBoardReflectivity 
							: 1.0f];

			float r = random()/8589934588.0f; /* 4*(2**31-1) */

			/* draw one square */
			glBegin(GL_TRIANGLE_STRIP);
			glTexCoord2f(0.0f+r, 0.0f+r);
			glVertex3f(x * 10.0f - 40.0f, 0.0f, 40.0f - y * 10.0f);
			glTexCoord2f(0.5f+r, 0.0f+r);
			glVertex3f(x * 10.0f - 30.0f, 0.0f, 40.0f - y * 10.0f);
			glTexCoord2f(0.0f+r, 0.5f+r);
			glVertex3f(x * 10.0f - 40.0f, 0.0f, 30.0f - y * 10.0f);
			glTexCoord2f(0.5f+r, 0.5f+r);
			glVertex3f(x * 10.0f - 30.0f, 0.0f, 30.0f - y * 10.0f);
			glEnd();
			
#if 0
			if (!overReflection) {
				glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
				glBegin(GL_QUADS);
				glTexCoord2f(0.0f+r, 0.0f+r);
				glVertex3f(x * 10.0f - 40.0f, 0.0f, 40.0f - y * 10.0f);
				glTexCoord2f(0.5f+r, 0.0f+r);
				glVertex3f(x * 10.0f - 30.0f, 0.0f, 40.0f - y * 10.0f);
				glTexCoord2f(0.5f+r, 0.5f+r);
				glVertex3f(x * 10.0f - 30.0f, 0.0f, 30.0f - y * 10.0f);
				glTexCoord2f(0.0f+r, 0.5f+r);
				glVertex3f(x * 10.0f - 40.0f, 0.0f, 30.0f - y * 10.0f);
				glEnd();
				glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
			}
#endif
		}
	}

	if (overReflection) {
		glPopAttrib();
		
		return;
	}


	//
	// Draw border
	//
	const float IB = kBoardRadius; 		// Inside border
	const float OB = IB+kBorderWidth; 	// Outside border
	const float DP =  5.0f;				// Depth
	const float TO = 0.5f*(1.0f - IB/OB); 	// Texture offset

	[fBorderDrawStyle startStyle:1.0f];

	//
	// Front
	//
	glBegin(GL_TRIANGLE_STRIP);
	glNormal3f(0.0f, 1.0f, 0.0f);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(-OB, 0.0f, +OB);
	glTexCoord2f(TO, 0.0f);
	glVertex3f(-IB, 0.0f, +OB);
	glTexCoord2f(TO, 1.0f);
	glVertex3f(-IB, 0.0f, +IB);
	glTexCoord2f(1.0f-TO, 0.0f);
	glVertex3f(+IB, 0.0f, +OB);
	glTexCoord2f(1.0f-TO, 1.0f);
	glVertex3f(+IB, 0.0f, +IB);
	glTexCoord2f(1.0f, 0.0f);
	glVertex3f(+OB, 0.0f, +OB);
	glEnd();
	glBegin(GL_TRIANGLE_STRIP);
	glNormal3f(0.0f, 0.0f, 1.0f);
	glTexCoord2f(0.0f, 1.0f);
	glVertex3f(-OB, 0.0f, +OB);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(-OB,  -DP, +OB);
	glTexCoord2f(1.0f, 1.0f);
	glVertex3f(+OB, 0.0f, +OB);
	glTexCoord2f(1.0f, 0.0f);
	glVertex3f(+OB,  -DP, +OB);
	glEnd();
	//
	// Back
	//
	glBegin(GL_TRIANGLE_STRIP);
	glNormal3f(0.0f, 1.0f, 0.0f);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(-OB, 0.0f, -OB);
	glTexCoord2f(TO, 1.0f);
	glVertex3f(-IB, 0.0f, -IB);
	glTexCoord2f(TO, 0.0f);
	glVertex3f(-IB, 0.0f, -OB);
	glTexCoord2f(1.0f-TO, 1.0f);
	glVertex3f(+IB, 0.0f, -IB);
	glTexCoord2f(1.0f-TO, 0.0f);
	glVertex3f(+IB, 0.0f, -OB);
	glTexCoord2f(1.0f, 0.0f);
	glVertex3f(+OB, 0.0f, -OB);
	glEnd();
	glBegin(GL_TRIANGLE_STRIP);
	glNormal3f(0.0f, 0.0f, -1.0f);
	glTexCoord2f(0.0f, 1.0f);
	glVertex3f(-OB, 0.0f, -OB);
	glTexCoord2f(1.0f, 1.0f);
	glVertex3f(+OB, 0.0f, -OB);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(-OB,  -DP, -OB);
	glTexCoord2f(1.0f, 0.0f);
	glVertex3f(+OB,  -DP, -OB);
	glEnd();
	//
	// Left
	//
	glBegin(GL_TRIANGLE_STRIP);
	glNormal3f(0.0f, 1.0f, 0.0f);
	glTexCoord2f(1.0f, 0.0f);
	glVertex3f(-OB, 0.0f, +OB);
	glTexCoord2f(1.0f-TO, 1.0f);
	glVertex3f(-IB, 0.0f, +IB);
	glTexCoord2f(1.0f-TO, 0.0f);
	glVertex3f(-OB, 0.0f, +IB);
	glTexCoord2f(TO, 1.0f);
	glVertex3f(-IB, 0.0f, -IB);
	glTexCoord2f(TO, 0.0f);
	glVertex3f(-OB, 0.0f, -IB);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(-OB, 0.0f, -OB);
	glEnd();
	glBegin(GL_TRIANGLE_STRIP);
	glNormal3f(-1.0f, 0.0f, 0.0f);
	glTexCoord2f(1.0f, 1.0f);
	glVertex3f(-OB, 0.0f, +OB);
	glTexCoord2f(0.0f, 1.0f);
	glVertex3f(-OB, 0.0f, -OB);
	glTexCoord2f(1.0f, 0.0f);
	glVertex3d(-OB,  -DP, +OB);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3d(-OB,  -DP, -OB);
	glEnd();
	//
	// Right
	//
	glBegin(GL_TRIANGLE_STRIP);
	glNormal3f(0.0f, 1.0f, 0.0f);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(+OB, 0.0f, +OB);
	glTexCoord2f(TO, 0.0f);
	glVertex3f(+OB, 0.0f, +IB);
	glTexCoord2f(TO, 1.0f);
	glVertex3f(+IB, 0.0f, +IB);
	glTexCoord2f(1.0f-TO, 0.0f);
	glVertex3f(+OB, 0.0f, -IB);
	glTexCoord2f(1.0f-TO, 1.0f);
	glVertex3f(+IB, 0.0f, -IB);
	glTexCoord2f(1.0f, 0.0f);
	glVertex3f(+OB, 0.0f, -OB);
	glEnd();
	glBegin(GL_TRIANGLE_STRIP);
	glNormal3f(1.0f, 0.0f, 0.0f);
	glTexCoord2f(0.0f, 1.0f);
	glVertex3f(+OB, 0.0f, +OB);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3d(+OB,  -DP, +OB);
	glTexCoord2f(1.0f, 1.0f);
	glVertex3f(+OB, 0.0f, -OB);
	glTexCoord2f(1.0f, 0.0f);
	glVertex3d(+OB,  -DP, -OB);
	glEnd();

	glPopAttrib();
}

/* Draws the co-ordinates around the edge of the board */
- (void) drawCoords
{
	glPushAttrib(GL_LIGHTING | GL_TEXTURE_BIT | GL_ENABLE_BIT);

	glEnable(GL_TEXTURE_2D);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glEnable(GL_BLEND);
	glDisable(GL_DEPTH_TEST);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDisable(GL_LIGHTING);

	const float kSize	= 3.5f;
	const float kNHOff	= 1.00f;
	const float kNVOff	= 3.25f;
	const float kLHOff	= 3.25f;
	const float kLVOff	= 1.00f;

	/* Draw the numbers, always on the left and upright, no matter
	   which color we're playing
	*/
	glColor4f(1.0f, 1.0f, 1.0f, fLabelIntensity);
	for (int rows = 0; rows < 8; rows++)  {
 		glBindTexture(GL_TEXTURE_2D, fNumberTextures[rows]);

		float	t,l,b,r;

		if ([self facingWhite]) {
			l = -(40.0f + kNHOff + kSize);
			r = -(40.0f + kNHOff);
			t = 40.0f - kNVOff - rows*10.0f - kSize;
			b = 40.0f - kNVOff - rows*10.0f;
		} else {
			r = -(40.0f + kNHOff + kSize);
			l = -(40.0f + kNHOff);
			b = 40.0f - kNVOff - rows*10.0f - kSize;
			t = 40.0f - kNVOff - rows*10.0f;
		}
		glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(l, 0.0f, b);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(r, 0.0f, b);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(r, 0.0f, t);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(l, 0.0f, t);
		glEnd();
	}

	/* Draw the letters */
	for (int cols = 0; cols < 8; cols++) {
		glBindTexture(GL_TEXTURE_2D, fLetterTextures[cols]);

		float	t,l,b,r;

		if ([self facingWhite]) {
			t = 40.0f + kLVOff;
			b = 40.0f + kLVOff + kSize;
			l = cols*10.f + kLHOff - 40.0f;
			r = cols*10.f + kLHOff - 40.0f + kSize;
		} else {
			t = -(40.0f + kLVOff);
			b = -(40.0f + kLVOff + kSize);
			r = cols*10.f + kLHOff - 40.0f;
			l = cols*10.f + kLHOff - 40.0f + kSize;
		}
		glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(l, 0.0f, b);
		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(r, 0.0f, b);
		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(r, 0.0f, t);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(l, 0.0f, t);
		glEnd();
	}

	glPopAttrib();
}

- (void) setupPieceDrawing:(BOOL)white reflect:(BOOL)reflection alpha:(float)alpha
{
	glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	if (reflection)
		alpha *= fBoardReflectivity;
	[fPieceDrawStyle[!white] startStyle:alpha];
	glDepthMask(alpha > 0.5f);
}

- (void) endPieceDrawing
{
	glPopAttrib();
}

/* Draws a single piece */
- (void) simplyDrawPiece:(MBCPiece)piece at:(MBCPosition)pos scale:(float)scale
{
	bool		wkr 	= false; /* white knight rotate flag */
	int 		color	= Color(piece) != 0;
	piece				= Piece(piece);

	if (!color) 			/* white */
		if (piece == KNIGHT)	/* white knight */
			wkr = true;		/* white knight */

	glPushMatrix();
	glTranslatef(pos[0], pos[1], pos[2]);
	if (wkr)		/* is white knight */
		glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
	glScalef(scale, scale, scale);
	glCallList(piece);
	glPopMatrix();

	fLastPieceDrawn	= piece;
}

- (void) drawPiece:(MBCPiece)piece at:(MBCPosition)pos scale:(float)scale reflect:(BOOL)reflection alpha:(float)alpha
{
	[self setupPieceDrawing:!Color(piece) reflect:reflection alpha:alpha];
	[self simplyDrawPiece:piece at:pos scale:scale];
	[self endPieceDrawing];
}

- (void) drawPiece:(MBCPiece)piece at:(MBCPosition)pos reflect:(BOOL)reflection alpha:(float)alpha
{
	[self setupPieceDrawing:!Color(piece) reflect:reflection alpha:alpha];
	[self simplyDrawPiece:piece at:pos scale:1.0f];
	[self endPieceDrawing];
}

- (void) drawPiece:(MBCPiece)piece at:(MBCPosition)pos reflect:(BOOL)reflection
{
	[self setupPieceDrawing:!Color(piece) reflect:reflection alpha:1.0f];
	[self simplyDrawPiece:piece at:pos scale:1.0f];
	[self endPieceDrawing];
} 

- (void) drawPiece:(MBCPiece)piece at:(MBCPosition)pos scale:(float)scale
{
	[self setupPieceDrawing:!Color(piece) reflect:NO alpha:1.0f];
	[self simplyDrawPiece:piece at:pos scale:scale];
	[self endPieceDrawing];
}

/* Draws the pieces */
- (void) drawPieces:(BOOL)reflection
{
	for (MBCSquare square = 0; square<64; ++square) {
		MBCPiece  piece = fInAnimation 
			? [fBoard oldContents:square]
			: [fBoard curContents:square];
		if (fSelectedPiece && square == fSelectedSquare)
				continue;	// Skip original position of selected piece
		if (piece) {
			const MBCPosition pos = [self squareToPosition:square];
			float dist			  = 
				fSelectedPiece && (!fInAnimation || square == fSelectedDest)
				? fSelectedPos.FlatDistance(pos) 
				: 100.0f;
			const float	kProximity = 5.0f;
			if (dist < kProximity)
				[self drawPiece:piece at:pos reflect:reflection
					  alpha:pow(dist/kProximity, 4.0)];
			else	
				[self drawPiece:piece at:pos reflect:reflection];
		}
	}
}


/* Draw the selected piece (may be off grid) */
- (void) drawSelectedPiece:(BOOL)reflection
{
	[self drawPiece:fSelectedPiece at:fSelectedPos reflect:reflection];
}

/* Draw the promotion piece (transparent) */
- (void) drawPromotionPiece
{
	MBCPiece 	piece = EMPTY;
	MBCPosition pos;

	fPromotionSide = kNeitherSide;

	if (fSide == kWhiteSide || fSide == kBothSides)
		if ([fBoard canPromote:kWhiteSide]) {
			piece			=	[fBoard defaultPromotion:YES];
			fPromotionSide	=	kWhiteSide;
			pos[0]			= 	-kPromotionPieceX;
			pos[1]			= 	  0.0f;
			pos[2]			=   -kPromotionPieceZ;
		} 
	if (fSide == kBlackSide || fSide == kBothSides)
		if ([fBoard canPromote:kBlackSide]) {
			piece			=	[fBoard defaultPromotion:NO];
			fPromotionSide	= 	kBlackSide;
			pos[0]			= 	kPromotionPieceX;
			pos[1]			= 	  0.0f;
			pos[2]			=   kPromotionPieceZ;
		}
	if (fPromotionSide == kNeitherSide)
		return;
	
	bool		wkr		= (fPromotionSide == kWhiteSide && piece == KNIGHT);

	glPushAttrib(GL_ENABLE_BIT);
	
	[fSelectedPieceDrawStyle startStyle:1.0f];

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glPushMatrix();
	glTranslatef(pos[0], pos[1], pos[2]);
	if (wkr)			/* is white knight */
		glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
	glCallList(Piece(piece));
	glPopMatrix();

	glPopAttrib();
}

- (void) placeLights
{
	const float kDiffuse = 0.75;
	GLfloat l_diffuse[4] = { kDiffuse, kDiffuse, kDiffuse, 1.0 };
	GLfloat l_ambient[4] = { fAmbient, fAmbient, fAmbient, 0.0 };

	glEnable(GL_LIGHT0);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, l_diffuse);
	glLightfv(GL_LIGHT0, GL_AMBIENT, l_ambient);
	glLightfv(GL_LIGHT0, GL_SPECULAR, l_diffuse);
	glLightfv(GL_LIGHT0, GL_POSITION, fLightPos);

	if (fPickedSquare != kInvalidSquare) {
		GLfloat spot_color[4]		= { 1.0, 1.0, 1.0, 1.0 };
		GLfloat spot_pos[4] 		= { 0.0, 100.0, 0.0, 1.0};
		GLfloat spot_direction[3]   = { 0.0, -1.0, 0.0 };

		MBCPosition pickedPos = [self squareToPosition:fPickedSquare];

		spot_pos[0]	= pickedPos[0];
		spot_pos[2] = pickedPos[2];

		glEnable(GL_LIGHT1);
		glLightfv(GL_LIGHT1, GL_DIFFUSE, spot_color);
		glLightfv(GL_LIGHT1, GL_SPECULAR, spot_color);
		glLightfv(GL_LIGHT1, GL_POSITION, spot_pos);
		glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, spot_direction);
		glLighti(GL_LIGHT1, GL_SPOT_EXPONENT, 100);
		glLighti(GL_LIGHT1, GL_SPOT_CUTOFF, 5);
		glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 0.0f);
		glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0001f);
	} else 
		glDisable(GL_LIGHT1);
}

MBCPieceCode gInHandOrder[] = {PAWN, BISHOP, KNIGHT, ROOK, QUEEN};

- (void) drawPiecesInHand
{
	const float kLabelX 	=  42.0f;
	const float kLabelSize	=  4.0f;
	const float	kLabelLeft	=  kLabelX;
	const float kLabelRight	=  kLabelX+kLabelSize;
	const float kSpacing	=  kInHandPieceSize;
	const float kLabelZ		=  4.0f;
	const float kPieceX		=  kInHandPieceX;
	const float kPieceZ		=  kInHandPieceZOffset+kInHandPieceSize/2.0f;
	const float kScale		=  0.95f;
	const bool  kFlip		=  fAzimuth < 90.0f || fAzimuth >= 270.0f;
	const float	kTexLeft	=  kFlip ? 1.0f : 0.0f;
	const float kTexRight	=  1.0f-kTexLeft;
	const float kTexBottom	=  kFlip ? 1.0f : 0.0f;
	const float kTexTop		=  1.0f-kTexBottom;

	glPushAttrib(GL_LIGHTING | GL_ENABLE_BIT | GL_TEXTURE_BIT);

	glEnable(GL_TEXTURE_2D);
	glEnable(GL_BLEND);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDisable(GL_LIGHTING);
	glDisable(GL_DEPTH_TEST);

	/* Draw the numbers */
	glColor4f(1.0f, 1.0f, 1.0f, fLabelIntensity);

	for (int p = 0; p<5; ++p) {
		MBCPiece piece = White(gInHandOrder[p]);
		int numInHand = fInAnimation 
			? [fBoard oldInHand:piece] 
			: [fBoard curInHand:piece];
		numInHand -= (piece+kInHandSquare == fSelectedSquare);
		if (numInHand > 1) {
			glBindTexture(GL_TEXTURE_2D, fNumberTextures[min(numInHand,8)-1]);

			float z	= p * kSpacing;

			glBegin(GL_QUADS);
			glTexCoord2f(kTexLeft, kTexBottom);
			glVertex3f(kLabelLeft, 0.0f,  kLabelZ+z+kLabelSize);
			glTexCoord2f(kTexRight, kTexBottom);
			glVertex3f(kLabelRight, 0.0f, kLabelZ+z+kLabelSize);
			glTexCoord2f(kTexRight, kTexTop);
			glVertex3f(kLabelRight, 0.0f, kLabelZ+z);
			glTexCoord2f(kTexLeft, kTexTop);
			glVertex3f(kLabelLeft, 0.0f,  kLabelZ+z);
			glEnd();			
		}
	}

	for (int p = 0; p<5; ++p) {
		MBCPiece piece = Black(gInHandOrder[p]);
		int numInHand = fInAnimation 
			? [fBoard oldInHand:piece] 
			: [fBoard curInHand:piece];
		numInHand -= (piece+kInHandSquare == fSelectedSquare);
		if (numInHand > 1) {
			glBindTexture(GL_TEXTURE_2D, fNumberTextures[min(numInHand,8)-1]);

			float z	= p * kSpacing;

			glBegin(GL_QUADS);
			glTexCoord2f(kTexLeft, kTexBottom);
			glVertex3f(kLabelLeft, 0.0f,  -kLabelZ-z);
			glTexCoord2f(kTexRight, kTexBottom);
			glVertex3f(kLabelRight, 0.0f, -kLabelZ-z);
			glTexCoord2f(kTexRight, kTexTop);
			glVertex3f(kLabelRight, 0.0f, -kLabelZ-z-kLabelSize);
			glTexCoord2f(kTexLeft, kTexTop);
			glVertex3f(kLabelLeft, 0.0f,  -kLabelZ-z-kLabelSize);
			glEnd();			
		}
	}

	glDisable(GL_BLEND);

	glPopAttrib();

	[self placeLights];

	for (int p = 0; p<5; ++p) {
		MBCPiece piece = White(gInHandOrder[p]);
		int numInHand = fInAnimation 
			? [fBoard oldInHand:piece] 
			: [fBoard curInHand:piece];
		numInHand -= (piece+kInHandSquare == fSelectedSquare);
		if (numInHand) {
			MBCPosition pos = {{kPieceX, 0.0f,  kPieceZ}};
			pos[2]		   += p*kSpacing;

			[self drawPiece:piece at:pos scale:kScale];			
		}
	}

	for (int p = 0; p<5; ++p) {
		MBCPiece piece = Black(gInHandOrder[p]);
		int numInHand = fInAnimation 
			? [fBoard oldInHand:piece] 
			: [fBoard curInHand:piece];
		numInHand -= (piece+kInHandSquare == fSelectedSquare);
		if (numInHand) {
			MBCPosition pos = {{kPieceX, 0.0f,  -kPieceZ}};
			pos[2]		   -= p*kSpacing;

			[self drawPiece:piece at:pos scale:kScale];			
		}
	}
	glPopMatrix();
}

- (void) drawArrowFrom:(MBCPosition)fromPos to:(MBCPosition)toPos width:(float)w
{
	glPushAttrib(GL_ENABLE_BIT);	/* Save states */
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_LIGHTING);
	glEnable(GL_BLEND);
   	glDisable(GL_CULL_FACE);	    /* Too lazy to figure out winding */
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	const float	kStemW  = w;
	const float	kPointW	= 2.00f*w;
	const float	kPointH	= 1.50f*w;
	const float	kHeight	= 0.01f;

	const float len				= 
		hypot(toPos[0]-fromPos[0], toPos[2]-fromPos[2]);
	const float	alpha			= 
		atan2(toPos[2]-fromPos[2], toPos[0]-fromPos[0]);
	const float sinAlpha		= sin(alpha);
	const float cosAlpha		= cos(alpha);

	MBCPosition p1	=	fromPos;
	p1.pos[0]	   -= 	kStemW*sinAlpha;
	p1.pos[1]		= 	kHeight;
	p1.pos[2]	   += 	kStemW*cosAlpha;
	MBCPosition p2	=	fromPos;
	p2.pos[0]	   += 	kStemW*sinAlpha;
	p2.pos[1]		= 	kHeight;
	p2.pos[2]	   -= 	kStemW*cosAlpha;
	MBCPosition p3	=	p1;
	p3.pos[0]	   += 	(len-kPointH)*cosAlpha;
	p3.pos[2]	   += 	(len-kPointH)*sinAlpha;
	MBCPosition p4	=	p2;
	p4.pos[0]	   += 	(len-kPointH)*cosAlpha;
	p4.pos[2]	   += 	(len-kPointH)*sinAlpha;
	MBCPosition p5	=	p3;
	p5.pos[0]	   -= 	(kPointW-kStemW)*sinAlpha;
	p5.pos[2]	   += 	(kPointW-kStemW)*cosAlpha;
	MBCPosition p6	=	p4;
	p6.pos[0]	   += 	(kPointW-kStemW)*sinAlpha;
	p6.pos[2]	   -= 	(kPointW-kStemW)*cosAlpha;
	MBCPosition p7	=	toPos;
	p7.pos[1]		= 	kHeight;

	glBegin(GL_TRIANGLES);
	glVertex3fv(p1);
	glVertex3fv(p2);
	glVertex3fv(p4);
	glVertex3fv(p4);
	glVertex3fv(p3);
	glVertex3fv(p1);
	glVertex3fv(p5);
	glVertex3fv(p6);
	glVertex3fv(p7);
	glEnd();

	glPopAttrib();
}

- (void) drawMove:(MBCMove *)move asHint:(BOOL)hint
{
	if (!move)
		return;

	MBCPosition	fromPos	= [self squareToPosition: move->fFromSquare];
	MBCPosition	toPos	= [self squareToPosition: move->fToSquare];

	if (hint) 
		glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
	else
		glColor4f(0.0f, 0.0f, 1.0f, 0.5f);
	
	[self drawArrowFrom:fromPos to:toPos width:2.0f];
}

- (void) drawManipulator
{
	//
	// Save normal projection and superimpose an Ortho projection
	//
	glPushMatrix();
	glLoadIdentity();
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	NSRect b = [self bounds];
	gluOrtho2D(NSMinX(b), NSMaxX(b), NSMinY(b), NSMaxY(b));
	glMatrixMode(GL_MODELVIEW);
    glRotatef(-90.0, 1.0, 0.0, 0.0);
	//
	// Draw navigation aid
	//
	bool	    horizontal		= 
		fabs(fCurMouse.x-fOrigMouse.x) > fabs(fCurMouse.y-fOrigMouse.y);
	const float	kUserSpaceScale		= [[self window] userSpaceScaleFactor];
	const float	kScale			= kUserSpaceScale*kUserSpaceScale;
	const float	kCircleSize		= 10.0f*kScale;
	const float	kArrowClearance	= 15.0f*kScale;
	const float	kArrowLength	= 30.0f*kScale;
	const float kArrowWidth		= 10.0f*kScale;
	const float	kThreshold		= 10.0f*kScale;
	const float kWellSize		= 55.0f*kScale;
	const float kWellRound		= 20.0f*kScale;
	NSPoint kScaledMouse	 = NSMakePoint(fOrigMouse.x*kUserSpaceScale, fOrigMouse.y*kUserSpaceScale);

	GLfloat on_color[4] 		= {1.0f, 1.0f, 1.0f, 1.0f};
	GLfloat off_color[4] 		= {1.0f, 1.0f, 1.0f, 0.4f};
	GLfloat	well_color[4]		= {0.5f, 0.5f, 0.5f, 0.6f};
	//
	// Well & Circle
	//
	glPushAttrib(GL_ENABLE_BIT);	/* Save states */
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_LIGHTING);
	glEnable(GL_BLEND);
   	glDisable(GL_CULL_FACE);	    /* Too lazy to figure out winding */
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	GLUquadricObj * q 		= gluNewQuadric();

	glPushMatrix();
    glRotatef(90.0, 1.0, 0.0, 0.0);
	glTranslatef(kScaledMouse.x, kScaledMouse.y, 0.01f);
	
	glColor4fv(well_color);
	glBegin(GL_QUADS);
	glVertex3f(-kWellSize+kWellRound, -kWellSize, 0.0f);
	glVertex3f( kWellSize-kWellRound, -kWellSize, 0.0f);
	glVertex3f( kWellSize-kWellRound, -kWellSize+kWellRound, 0.0f);
	glVertex3f(-kWellSize+kWellRound, -kWellSize+kWellRound, 0.0f);
	glVertex3f(-kWellSize, -kWellSize+kWellRound, 0.0f);
	glVertex3f( kWellSize, -kWellSize+kWellRound, 0.0f);
	glVertex3f( kWellSize,  kWellSize-kWellRound, 0.0f);
	glVertex3f(-kWellSize,  kWellSize-kWellRound, 0.0f);
	glVertex3f(-kWellSize+kWellRound, kWellSize-kWellRound, 0.0f);
	glVertex3f( kWellSize-kWellRound, kWellSize-kWellRound, 0.0f);
	glVertex3f( kWellSize-kWellRound, kWellSize, 0.0f);
	glVertex3f(-kWellSize+kWellRound, kWellSize, 0.0f);
	glEnd();
	glTranslatef(-kWellSize+kWellRound, -kWellSize+kWellRound, 0.0f);
	gluPartialDisk(q, 0.0, kWellRound, 10, 1, 180.0, 90.0);
	glTranslatef(2.0*(kWellSize-kWellRound), 0.0f, 0.0f);
	gluPartialDisk(q, 0.0, kWellRound, 10, 1,  90.0, 90.0);
	glTranslatef(0.0, 2.0*(kWellSize-kWellRound), 0.0f);
	gluPartialDisk(q, 0.0, kWellRound, 10, 1,   0.0, 90.0);
	glTranslatef(-2.0*(kWellSize-kWellRound), 0.0f, 0.0f);
	gluPartialDisk(q, 0.0, kWellRound, 10, 1, 270.0, 90.0);
	glTranslatef( kWellSize-kWellRound, -kWellSize+kWellRound, 0.0f);

	glColor4fv(fabs(fCurMouse.x-fOrigMouse.x)<kThreshold 
			&& fabs(fCurMouse.y-fOrigMouse.y)<kThreshold 
			   ? on_color : off_color);
	gluDisk(q, 0.0, kCircleSize, 10, 1);
	glPopMatrix();
	gluDeleteQuadric(q);

	MBCPosition	fromPos, toPos;

	//
	// Up
	//
	fromPos[0] = kScaledMouse.x;
	fromPos[1] = 0;
	fromPos[2] = kScaledMouse.y+kArrowClearance;
	toPos	   = fromPos;
	toPos[2]  += kArrowLength;
	glColor4fv((!horizontal && (fCurMouse.y > fOrigMouse.y+kThreshold))
			   ? on_color : off_color);
	[self drawArrowFrom:fromPos to:toPos width:kArrowWidth];

	//
	// Down
	//
	fromPos[0] = kScaledMouse.x;
	fromPos[1] = 0;
	fromPos[2] = kScaledMouse.y-kArrowClearance;
	toPos	   = fromPos;
	toPos[2]  -= kArrowLength;
	glColor4fv((!horizontal && (fCurMouse.y < fOrigMouse.y-kThreshold))
			   ? on_color : off_color);
	[self drawArrowFrom:fromPos to:toPos width:kArrowWidth];

	//
	// Right
	//
	fromPos[0] = kScaledMouse.x+kArrowClearance;
	fromPos[1] = 0;
	fromPos[2] = kScaledMouse.y;
	toPos	   = fromPos;
	toPos[0]  += kArrowLength;
	glColor4fv((horizontal && (fCurMouse.x > fOrigMouse.x+kThreshold))
			   ? on_color : off_color);
	[self drawArrowFrom:fromPos to:toPos width:kArrowWidth];

	//
	// Left
	//
	fromPos[0] = kScaledMouse.x-kArrowClearance;
	fromPos[1] = 0;
	fromPos[2] = kScaledMouse.y;
	toPos	   = fromPos;
	toPos[0]  -= kArrowLength;
	glColor4fv((horizontal && (fCurMouse.x < fOrigMouse.x-kThreshold))
			   ? on_color : off_color);
	[self drawArrowFrom:fromPos to:toPos width:kArrowWidth];

	glPopAttrib();
	//
	// Restore projection
	//
	glPopMatrix();	
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
}

- (void) makeBoardSolid
{
	//
	// If we're in a transparent window, we have to make sure that the
	// board itself always remains opaque, no matter what blending we've
	// done with it
	//
	const float IB = kBoardRadius; 		// Inside border
	const float OB = IB+kBorderWidth; 	// Outside border
	const float DP =  5.0f;				// Depth

	glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT);
	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
	glDisable(GL_BLEND);
	glDisable(GL_LIGHTING);
   	glDisable(GL_CULL_FACE);	    /* Too lazy to figure out winding */
	glDisable(GL_DEPTH_TEST);
	glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
	glBegin(GL_QUADS);
	glVertex3f(-OB, 0.0f,  OB);
	glVertex3f( OB, 0.0f,  OB);
	glVertex3f( OB, 0.0f, -OB);
	glVertex3f(-OB, 0.0f, -OB);
	glEnd();	
	glBegin(GL_QUAD_STRIP);
	glVertex3f(-OB,  -DP,  OB);
	glVertex3f(-OB, 0.0f,  OB);
	glVertex3f( OB,  -DP,  OB);
	glVertex3f( OB, 0.0f,  OB);
	glVertex3f( OB,  -DP, -OB);
	glVertex3f( OB, 0.0f, -OB);
	glVertex3f(-OB,  -DP, -OB);
	glVertex3f(-OB, 0.0f, -OB);
	glEnd();
	glPopAttrib();
}

/* Draw the scene for a game */
- (void) drawPosition
{
	if (fIsFloating) {
		[[NSColor clearColor] set];
		NSRectFill([self bounds]);
	}

	if (fNeedPerspective)
		[self setupPerspective];

	/* Clear the buffers */
	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	if (fBoardReflectivity)
		glClear(GL_STENCIL_BUFFER_BIT);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	/* Place lights */
	[self placeLights];

	/* Draw the board */
   	[self drawBoard:NO];

	/* Make a stencil of the floor if reflections are done */
	if (fBoardReflectivity) {
		/* Save the old scene attributes */
		glPushAttrib(GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT);

		/* Disable stuff */
		glDisable(GL_DEPTH_TEST);
		glDisable(GL_LIGHTING);
		glDisable(GL_BLEND);

		/* Don't draw to the screen or the depth buffer at this moment */
		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
		glDepthMask(GL_FALSE);

		/* Write to the stencil buffer */
		glEnable(GL_STENCIL_TEST);
		glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
		glStencilFunc(GL_ALWAYS, 1, 0xffffffff);

		glBegin(GL_QUADS);
		glVertex3f(-40.0f, 0.0f,  40.0f);
		glVertex3f( 40.0f, 0.0f,  40.0f);
		glVertex3f( 40.0f, 0.0f, -40.0f);
		glVertex3f(-40.0f, 0.0f, -40.0f);
		glEnd();

		// 
		// Re-enable writing to the depth buffer and to the color channels
		// but NOT to the alpha channel in case we have a translucent window
		//
		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
		glDepthMask(GL_TRUE);

		/* Draw only if stencil is set to 1 */
		glStencilFunc(GL_EQUAL, 1, 0xffffffff);
		glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

		/* draw the reflected pieces */

		/* Reflect in floor */
		glPushMatrix(); {
			glScalef(1.0f, -1.0f, 1.0f);

			glEnable(GL_LIGHTING);
			[self placeLights];

			glCullFace(GL_FRONT); {
			    [self drawPieces:YES];

			    if (fSelectedPiece)
					[self drawSelectedPiece:YES];
			} glCullFace(GL_BACK);
		} glPopMatrix();

		/* Restore the scene attributes */
		glPopAttrib();

		/* Now blend board back into the reflections */
		[self drawBoard:YES];
	}

	/* Draw the co-ordinates [1-8] [a-h] */
	[self drawCoords];

	/* Draw hint and last move */
	[self drawMove:fHintMove asHint:YES];
	[self drawMove:fLastMove asHint:NO];

	glDisable(GL_BLEND);

	/* Draw the pieces */
	[self drawPieces:NO];

	if (fSelectedPiece) 
		[self drawSelectedPiece:NO];

	if (fVariant == kVarCrazyhouse)
		[self drawPiecesInHand];

	[self drawPromotionPiece];

#if 0
	//
	// Some graphics cards seem to mess up the lighting when the knight
	// or king model were the last ones drawn
	//
	if (fLastPieceDrawn == KING || fLastPieceDrawn == KNIGHT) {
		MBCPosition pos = {{0.0f, 0.0f, 0.0f}};
		[self drawPiece:PAWN at:pos reflect:NO alpha:0.0f];
	}
#endif

	if (fInBoardManipulation)
		[self drawManipulator];

	// Update the GL context
	if (fIsFloating) 
		[self makeBoardSolid];

	//
	// Work around OpenGL driver issue
	//
#ifdef __LP64__
	glFlush();
#endif

	[[self openGLContext] flushBuffer];
}

@end

// Local Variables:
// mode:ObjC
// End: