/* File: MBCBoardViewDraw.mm Contains: Draw chess board Copyright: © 2002-2012 by Apple 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 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 "MBCBoardViewModels.h" #import "MBCBoardViewTextures.h" #import #import #import #import #import 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 { NSRect bounds = [self convertRectToBacking:[self bounds]]; 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 w = bounds.size.width; const float h = bounds.size.height*1.1f*(fVariant==kVarCrazyhouse ? 1.15f : 1.0f); const float kAspect = std::max(1.0f, h / w); const float kDistance = 300.0f; const float kBoardSize = kAspect*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; glViewport(0, 0, (GLsizei)(bounds.size.width), (GLsizei)(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.25f; 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|GL_TEXTURE_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 = 3.0f; const float kLabelLeft = kLabelX; const float kLabelRight = kLabelX+kLabelSize; const float kSpacing = kInHandPieceSize; const float kLabelZ = 6.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 kScale = 1.0f; 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; 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) 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(); } - (void) drawGloss { // // 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); glPushAttrib(GL_ENABLE_BIT); /* Save states */ glDisable(GL_DEPTH_TEST); glDisable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glDisable(GL_CULL_FACE); glDisable(GL_BLEND); GLfloat kBaseColor[3] = {0.25f, 0.25f, 0.25f}; GLfloat kTopColor[3] = {0.6f, 0.6f, 0.6f}; GLfloat kMidColor[3] = {0.3f, 0.3f, 0.3f}; const int kSegments = 32; GLfloat kAspectRatio = b.size.width/b.size.height; GLfloat kMidX = b.size.width*0.5f; GLfloat kMidY = b.size.height*3.0f*kAspectRatio; GLfloat kSecY = b.size.height*0.55f; GLfloat kRadius = hypotf(kMidX, kMidY-kSecY); GLfloat kRadius2 = kRadius*kRadius; glColor3fv(kBaseColor); glBegin(GL_QUADS); glVertex3f(NSMinX(b), NSMinY(b), 0.0f); glVertex3f(NSMaxX(b), NSMinY(b), 0.0f); glVertex3f(NSMaxX(b), NSMaxY(b), 0.0f); glVertex3f(NSMinX(b), NSMaxY(b), 0.0f); glEnd(); glBegin(GL_TRIANGLE_STRIP); for (int i=0; i<=kSegments; ++i) { GLfloat x = b.origin.x+b.size.width*i/kSegments; GLfloat dx = kMidX-x; GLfloat y0 = kMidY-sqrtf(kRadius2-dx*dx); GLfloat y1 = b.origin.y+b.size.height; glColor3fv(kMidColor); glVertex3f(x, y0, 0.0f); glColor3fv(kTopColor); glVertex3f(x, y1, 0.0f); } glEnd(); // // Restore projection // glPopAttrib(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); } - (void) update { [super update]; if (![[self openGLContext] view]) { // // Fall back to a less memory hungry format. // [self pickPixelFormat:YES]; fNeedPerspective = true; } } /* Draw the scene for a game */ - (void) drawPosition { if (![[self openGLContext] view]) { // // Fall back to a less memory hungry format. // [self pickPixelFormat:YES]; fNeedPerspective = true; } if (fIsFloating) { [[NSColor clearColor] set]; NSRectFill([self bounds]); } if (fNeedStaticModels) { fNeedStaticModels = false; [self loadColors]; [self generateModelLists]; } 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); [self drawGloss]; /* 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 */ if (fVariant == kVarCrazyhouse) [self drawPiecesInHand]; [self drawPieces:NO]; if (fSelectedPiece) [self drawSelectedPiece:NO]; [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]; [self makeBoardSolid]; // // Work around OpenGL driver issue // #ifdef __LP64__ glFlush(); #endif [[self openGLContext] flushBuffer]; } @end // Local Variables: // mode:ObjC // End: