MBCBoardViewDraw.mm [plain text]
/*
File: MBCBoardViewDraw.mm
Contains: Draw chess board
Copyright: © 2002-2005 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:
IMPORTANT: This Apple software is supplied to you by Apple Computer,
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms,
and subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the
Apple Software, with or without modifications, in source and/or binary
forms; provided that if you redistribute the Apple Software in its
entirety and without modifications, you must retain this notice and
the following text and disclaimers in all such redistributions of the
Apple Software. Neither the name, trademarks, service marks or logos
of Apple Computer, Inc. may be used to endorse or promote products
derived from the Apple Software without specific prior written
permission from Apple. Except as expressly stated in this notice, no
other rights or licenses, express or implied, are granted by Apple
herein, including but not limited to any patent rights that may be
infringed by your derivative works or by other works in which the
Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE,
HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING
NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#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
//
long 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 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)bounds.size.width, (long)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_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);
#if 0
//
// Work around an apparent bug in some graphics driver versions
// where the stencil buffer matters although the stencil test
// is disabled.
//
if (!fBoardReflectivity)
glClear(GL_STENCIL_BUFFER_BIT);
#endif
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();
#if 0
//
// Draw outline of border so boundaries are antialiased
//
glBegin(GL_LINE_STRIP);
glVertex3f(-OB, -DP, +OB);
glVertex3f(+OB, -DP, +OB);
glVertex3f(+OB, -DP, -OB);
glVertex3f(-OB, -DP, -OB);
glVertex3f(-OB, -DP, +OB);
glEnd();
glBegin(GL_LINE_STRIP);
glVertex3f(-OB, 0.0f, +OB);
glVertex3f(+OB, 0.0f, +OB);
glVertex3f(+OB, 0.0f, -OB);
glVertex3f(-OB, 0.0f, -OB);
glVertex3f(-OB, 0.0f, +OB);
glEnd();
glBegin(GL_LINE_STRIP);
glVertex3f(-IB, 0.0f, +IB);
glVertex3f(+IB, 0.0f, +IB);
glVertex3f(+IB, 0.0f, -IB);
glVertex3f(-IB, 0.0f, -IB);
glVertex3f(-IB, 0.0f, +IB);
glEnd();
#endif
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 kCircleSize = 10.0f;
const float kArrowClearance = 15.0f;
const float kArrowLength = 30.0f;
const float kArrowWidth = 10.0f;
const float kThreshold = 10.0f;
const float kWellSize = 55.0f;
const float kWellRound = 20.0f;
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(fOrigMouse.x, fOrigMouse.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] = fOrigMouse.x;
fromPos[1] = 0;
fromPos[2] = fOrigMouse.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] = fOrigMouse.x;
fromPos[1] = 0;
fromPos[2] = fOrigMouse.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] = fOrigMouse.x+kArrowClearance;
fromPos[1] = 0;
fromPos[2] = fOrigMouse.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] = fOrigMouse.x-kArrowClearance;
fromPos[1] = 0;
fromPos[2] = fOrigMouse.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];
[[self openGLContext] flushBuffer];
}
@end
// Local Variables:
// mode:ObjC
// End: