home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / python-support / gnome-games-data / glchess / scene / opengl / opengl.py < prev    next >
Encoding:
Python Source  |  2009-04-14  |  29.7 KB  |  910 lines

  1. # -*- coding: utf-8 -*-
  2. import math
  3. import os.path
  4. from gettext import gettext as _
  5.  
  6. import cairo
  7. from OpenGL.GL import *
  8. from OpenGL.GLU import *
  9.  
  10. from glchess.defaults import *
  11.  
  12. import glchess.scene
  13. import texture
  14. import new_models
  15. builtin_models = new_models
  16.  
  17. PIECE_MOVE_SPEED    = 50.0 # FIXME: Define units
  18. BOARD_ROTATION_TIME = 0.8
  19.  
  20. SQUARE_WIDTH      = 10.0
  21. BOARD_DEPTH       = 3.0
  22. BOARD_BORDER      = 5.0
  23. BOARD_CHAMFER     = 2.0
  24. BOARD_INNER_WIDTH = (SQUARE_WIDTH * 8.0)
  25. BOARD_OUTER_WIDTH = (BOARD_INNER_WIDTH + BOARD_BORDER * 2.0)
  26. OFFSET            = (BOARD_OUTER_WIDTH * 0.5)
  27.  
  28. LIGHT_AMBIENT_COLOUR  = (0.4, 0.4, 0.4, 1.0)
  29. LIGHT_DIFFUSE_COLOUR  = (0.7, 0.7, 0.7, 1.0)
  30. LIGHT_SPECULAR_COLOUR = (1.0, 1.0, 1.0, 1.0)
  31.  
  32. BOARD_AMBIENT   = (0.2, 0.2, 0.2, 1.0)
  33. BOARD_DIFFUSE   = (0.8, 0.8, 0.8, 1.0)
  34. BOARD_SPECULAR  = (1.0, 1.0, 1.0, 1.0)
  35. BOARD_SHININESS = 128.0
  36.  
  37. BACKGROUND_COLOUR    = (0.53, 0.63, 0.75, 0.0)
  38. BORDER_COLOUR        = (0.72, 0.33, 0.0)
  39. BLACK_SQUARE_COLOURS = {None:                               (0.8, 0.8, 0.8),
  40.                         glchess.scene.HIGHLIGHT_SELECTED:   (0.3, 1.0, 0.3),
  41.                         glchess.scene.HIGHLIGHT_CAN_MOVE:   (0.3, 0.3, 1.0),
  42.                         glchess.scene.HIGHLIGHT_THREATENED: (1.0, 0.8, 0.8),
  43.                         glchess.scene.HIGHLIGHT_CAN_TAKE:   (1.0, 0.3, 0.3)}
  44. WHITE_SQUARE_COLOURS = {None:                               (1.0, 1.0, 1.0),
  45.                         glchess.scene.HIGHLIGHT_SELECTED:   (0.2, 1.0, 0.0),
  46.                         glchess.scene.HIGHLIGHT_CAN_MOVE:   (0.2, 0.2, 0.8),
  47.                         glchess.scene.HIGHLIGHT_THREATENED: (1.0, 0.8, 0.8),
  48.                         glchess.scene.HIGHLIGHT_CAN_TAKE:   (1.0, 0.2, 0.2)}
  49.  
  50. import math
  51. def accFrustum(left, right, bottom, top,
  52.                near, far,
  53.                pixdx, pixdy,
  54.                eyedx, eyedy, 
  55.                focus):
  56.     viewport = glGetIntegerv(GL_VIEWPORT)
  57.  
  58.     xwsize = right - left
  59.     ywsize = top - bottom
  60.     dx = -(pixdx*xwsize/viewport[2] + eyedx*near/focus)
  61.     dy = -(pixdy*ywsize/viewport[3] + eyedy*near/focus)
  62.  
  63.     glFrustum(left + dx, right + dx, bottom + dy, top + dy, near, far)
  64.     glTranslatef(-eyedx, -eyedy, 0.0)
  65.  
  66. def accPerspective(fovy, aspect,
  67.                    near, far,
  68.                    pixdx, pixdy,
  69.                    eyedx, eyedy, 
  70.                    focus):
  71.     fov2 = ((fovy*math.pi) / 180.0) / 2.0
  72.     top = near / (math.cos(fov2) / math.sin(fov2))
  73.     bottom = -top
  74.     right = top * aspect
  75.     left = -right
  76.     accFrustum(left, right, bottom, top, near, far, pixdx, pixdy, eyedx, eyedy, focus)
  77.  
  78. class ChessPiece(glchess.scene.ChessPiece):
  79.     """
  80.     """    
  81.     scene     = None
  82.     
  83.     # The piece to render
  84.     chessSet  = None
  85.     name      = None
  86.     
  87.     # The algebraic square location
  88.     location  = ''
  89.     
  90.     # The OpenGL co-ordinate location
  91.     pos       = None
  92.     targetPos = None
  93.     
  94.     # Is the piece moving?
  95.     moving    = False
  96.     
  97.     # Delete once moved?
  98.     delete    = False
  99.     
  100.     def __init__(self, scene, chessSet, name, location, feedback):
  101.         """
  102.         """
  103.         self.scene = scene
  104.         self.feedback = feedback
  105.         self.chessSet = chessSet
  106.         self.name = name
  107.         self.location = location
  108.         self.pos = self.scene._coordToLocation(location)
  109.  
  110.     def move(self, coord, delete, animate = True):
  111.         """Extends glchess.scene.ChessPiece"""
  112.         if not coord:
  113.             self.scene.pieces.remove(self)
  114.             self.feedback.onDeleted()
  115.             return
  116.  
  117.         self.delete = delete
  118.         self.location = coord
  119.         self.targetPos = self.scene._coordToLocation(coord)
  120.         
  121.         # If already there then delete
  122.         if self.pos == self.targetPos:
  123.             self.targetPos = None
  124.             if delete:
  125.                 self.scene.pieces.remove(self)
  126.                 self.feedback.onDeleted()
  127.                 self.scene.feedback.onRedraw()
  128.             return
  129.             
  130.         # If not currently moving then start
  131.         if not self.moving:
  132.             self.scene._animationQueue.append(self)
  133.             self.moving = True
  134.             
  135.             # Start animation
  136.             if self.scene.animating is False:
  137.                 self.scene.animating = True
  138.                 self.scene.feedback.startAnimation()
  139.     
  140.     def draw(self, state = 'default'):
  141.         """
  142.         """
  143.         self.chessSet.drawPiece(self.name, state, self.scene)
  144.         
  145.     def animate(self, timeStep):
  146.         """
  147.         
  148.         Return True if the piece has moved otherwise False.
  149.         """
  150.         if self.targetPos is None:
  151.             return False
  152.         
  153.         if self.pos == self.targetPos:
  154.             self.targetPos = None
  155.             if self.delete:
  156.                 self.scene.pieces.remove(self)
  157.                 self.feedback.onDeleted()
  158.             return False
  159.         
  160.         # Get distance to target
  161.         dx = self.targetPos[0] - self.pos[0]
  162.         dy = self.targetPos[1] - self.pos[1]
  163.         dz = self.targetPos[2] - self.pos[2]
  164.         
  165.         # Get movement step in each direction
  166.         xStep = timeStep * PIECE_MOVE_SPEED
  167.         if xStep > abs(dx):
  168.             xStep = dx
  169.         else:
  170.             xStep *= cmp(dx, 0.0)
  171.         yStep = timeStep * PIECE_MOVE_SPEED
  172.         if yStep > abs(dy):
  173.             yStep = dy
  174.         else:
  175.             yStep *= cmp(dy, 0.0)
  176.         zStep = timeStep * PIECE_MOVE_SPEED
  177.         if zStep > abs(dz):
  178.             zStep = dz
  179.         else:
  180.             zStep *= cmp(dz, 0.0)
  181.             
  182.         # Move the piece
  183.         self.pos = (self.pos[0] + xStep, self.pos[1] + yStep, self.pos[2] + zStep)
  184.         return True
  185.  
  186. class Scene(glchess.scene.Scene):
  187.     """
  188.     """
  189.     # The viewport dimensions
  190.     viewportWidth    = 0
  191.     viewportHeight   = 0
  192.     viewportAspect   = 1.0
  193.     
  194.     animating        = False
  195.     
  196.     # Loading screen properties
  197.     throbberEnabled  = False
  198.     throbberAngle    = 0.0
  199.     
  200.     # The scene light position
  201.     lightPos         = None
  202.  
  203.     # The board angle in degrees
  204.     boardAngle       = 0.0
  205.     oldBoardAngle    = 0.0
  206.     targetBoardAngle = 0.0
  207.     
  208.     # OpenGL display list for the board and a flag to regenerate it
  209.     boardList        = None
  210.     regenerateBoard  = False
  211.     
  212.     # Texture objects for the board
  213.     whiteTexture     = None
  214.     blackTexture     = None
  215.  
  216.     # ...
  217.     pieces           = None
  218.     chessSets        = None
  219.     piecesMoving     = False
  220.     
  221.     # Dictionary of co-ordinates to highlight
  222.     highlights       = None
  223.     
  224.     _animationQueue  = []
  225.     
  226.     jitters = ((0.0, 0.0),)
  227.  
  228.     showNumbering = False
  229.     numberingTexture = None
  230.  
  231.     def __init__(self, feedback):
  232.         """Constructor for an OpenGL scene"""
  233.         self.feedback = feedback
  234.         self.lightPos = [100.0, 100.0, 100.0, 1.0]
  235.         self.pieces = []
  236.         self.highlights = {}
  237.         self._animationQueue = []
  238.         
  239.         self.chessSets = {'white': builtin_models.WhiteBuiltinSet(), 'black': builtin_models.BlackBuiltinSet()}
  240.         
  241.         self.whiteTexture = texture.Texture(os.path.join(TEXTURE_DIR, 'board.png'),
  242.                                             ambient = BOARD_AMBIENT, diffuse = BOARD_DIFFUSE,
  243.                                             specular = BOARD_SPECULAR, shininess = BOARD_SHININESS)
  244.         self.blackTexture = texture.Texture(os.path.join(TEXTURE_DIR, 'board.png'),
  245.                                             ambient = BOARD_AMBIENT, diffuse = BOARD_DIFFUSE,
  246.                                             specular = BOARD_SPECULAR, shininess = BOARD_SHININESS)
  247.  
  248.     def onRedraw(self):
  249.         """This method is called when the scene needs redrawing"""
  250.         pass
  251.     
  252.     def addChessPiece(self, chessSet, name, coord, feedback):
  253.         """Add a chess piece model into the scene.
  254.         
  255.         'chessSet' is the name of the chess set (string).
  256.         'name' is the name of the piece (string).
  257.         'coord' is the the chess board location of the piece in LAN format (string).
  258.         'feedback' is the feedback object (extends scene.ChessPieceFeedback).
  259.  
  260.         Returns a reference to this chess piece or raises an exception.
  261.         """
  262.         chessSet = self.chessSets[chessSet]
  263.         piece = ChessPiece(self, chessSet, name, coord, feedback)
  264.         self.pieces.append(piece)
  265.         
  266.         # Redraw the scene
  267.         self.feedback.onRedraw()
  268.         
  269.         return piece
  270.  
  271.     def setBoardHighlight(self, coords):
  272.         """Highlight a square on the board.
  273.         
  274.         'coords' is a dictionary of highlight types keyed by square co-ordinates.
  275.                  The co-ordinates are a tuple in the form (file,rank).
  276.                  If None the highlight will be cleared.
  277.         """
  278.         if coords is None:
  279.             self.highlights = {}
  280.         else:
  281.             self.highlights = coords.copy()
  282.             
  283.         # Regenerate the optimised board model
  284.         self.regenerateBoard = True
  285.  
  286.         self.feedback.onRedraw()
  287.  
  288.     def showBoardNumbering(self, showNumbering):
  289.         """Extends glchess.scene.Scene"""
  290.         self.showNumbering = showNumbering
  291.         self.feedback.onRedraw()
  292.  
  293.     def showSmooth(self, doSmooth):
  294.         if doSmooth:
  295.             #self.jitters = ((-0.25, 0.25), (0.25, -0.25))
  296.             self.jitters = ((0.0033922635, 0.3317967229), (0.2806016275, -0.2495619123), (-0.273817106, -0.086844639))
  297.             #self.jitters = ((-0.175, -0.25), (-0.375, 0.25), (0.375, -0.25), (0.125, 0.25))
  298.         else:
  299.             self.jitters = ((0.0, 0.0),)
  300.         self.feedback.onRedraw()
  301.  
  302.     def reshape(self, width, height):
  303.         """Resize the viewport into the scene.
  304.         
  305.         'width' is the width of the viewport in pixels.
  306.         'height' is the width of the viewport in pixels.
  307.         """
  308.         self.viewportWidth = int(width)
  309.         self.viewportHeight = int(height)
  310.         self.viewportAspect = float(self.viewportWidth) / float(self.viewportHeight)
  311.         glViewport(0, 0, self.viewportWidth, self.viewportHeight)
  312.         self.feedback.onRedraw()
  313.  
  314.     def setBoardRotation(self, angle, animate = True):
  315.         """Set the rotation on the board.
  316.         
  317.         'angle' is the angle the board should be drawn at in degress (float, [0.0, 360.0]).
  318.         """
  319.         self.targetBoardAngle = angle
  320.         
  321.         if not animate:
  322.             self.oldBoardAngle = self.boardAngle = angle
  323.             self.feedback.onRedraw()
  324.             return
  325.         
  326.         if self.animating is False:
  327.             self.animating = True
  328.             self.feedback.startAnimation()
  329.  
  330.     def animate(self, timeStep):
  331.         """Extends glchess.scene.Scene"""
  332.         redraw1 = self.animateThrobber(timeStep)
  333.         self.piecesMoving = self.animatePieces(timeStep)
  334.         redraw2 = self.animateRotation(timeStep)
  335.         if redraw1 or redraw2 or self.piecesMoving:
  336.             self.animating = True
  337.             self.feedback.onRedraw()
  338.         else:
  339.             self.animating = False
  340.         return self.animating
  341.  
  342.     def render(self):
  343.         """Render the scene.
  344.         
  345.         This requires an OpenGL context.
  346.         """
  347.         glClearColor(*BACKGROUND_COLOUR)
  348.         if len(self.jitters) > 1:
  349.             glClear(GL_ACCUM_BUFFER_BIT)
  350.         
  351.         glLightfv(GL_LIGHT0, GL_AMBIENT, LIGHT_AMBIENT_COLOUR)
  352.         glLightfv(GL_LIGHT0, GL_DIFFUSE, LIGHT_DIFFUSE_COLOUR)
  353.         glLightfv(GL_LIGHT0, GL_SPECULAR, LIGHT_SPECULAR_COLOUR)
  354.         
  355.         for jitter in self.jitters:
  356.             glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
  357.             
  358.             # Set the projection for this scene
  359.             glMatrixMode(GL_PROJECTION)
  360.             glLoadIdentity()
  361.             if len(self.jitters) > 1:
  362.                 accPerspective(60.0, self.viewportAspect, 0.1, 1000, jitter[0], jitter[1], 0, 0, 1)
  363.             else:
  364.                 gluPerspective(60.0, self.viewportAspect, 0.1, 1000)
  365.  
  366.             # Do camera and board rotation/translation
  367.             glMatrixMode(GL_MODELVIEW)
  368.             glLoadIdentity()
  369.             self.transformCamera()
  370.  
  371.             glLightfv(GL_LIGHT0, GL_POSITION, self.lightPos)            
  372.             glEnable(GL_LIGHTING)
  373.             glEnable(GL_LIGHT0)
  374.             
  375.             self.transformBoard()
  376.             
  377.             glEnable(GL_DEPTH_TEST)
  378.             glEnable(GL_CULL_FACE)
  379.         
  380.             glEnable(GL_TEXTURE_2D)
  381.             glEnable(GL_COLOR_MATERIAL)
  382.             self.drawBoard()
  383.             glDisable(GL_COLOR_MATERIAL)
  384.             glDisable(GL_TEXTURE_2D)
  385.         
  386.             if self.showNumbering:
  387.                 self.drawNumbering()
  388.         
  389.             # WORKAROUND: Mesa is corrupting polygons on the bottom of the models
  390.             # It could be because the depth buffer has a low bit depth?
  391.             glClear(GL_DEPTH_BUFFER_BIT)
  392.         
  393.             if self.throbberEnabled:
  394.                 self.drawThrobber()
  395.             else:
  396.                 self.drawPieces()
  397.             
  398.             if len(self.jitters) > 1:
  399.                 glAccum(GL_ACCUM, 1.0 / len(self.jitters))
  400.  
  401.         if len(self.jitters) > 1:
  402.             glAccum(GL_RETURN, 1)
  403.             
  404.     def getSquare(self, x, y):
  405.         """Find the chess square at a given 2D location.
  406.         
  407.         'x' is the number of pixels from the left of the scene to select.
  408.         'y' is the number of pixels from the bottom of the scene to select.
  409.         
  410.         This requires an OpenGL context.
  411.         
  412.         Return the co-ordinate in LAN format (string) or None if no square at this point.
  413.         """
  414.         # FIXME: Don't rely on this? It seems to get corrupt when multiple games are started
  415.         viewport = glGetIntegerv(GL_VIEWPORT)
  416.  
  417.         # Don't render to screen, just select
  418.         # Selection buffer is large in case we select multiple squares at once (it generates an exception)
  419.         glSelectBuffer(20)
  420.         glRenderMode(GL_SELECT)
  421.  
  422.         glInitNames()
  423.         
  424.         # Create pixel picking region near cursor location
  425.         glMatrixMode(GL_PROJECTION)
  426.         glLoadIdentity()
  427.         gluPickMatrix(x, (float(viewport[3]) - y), 1.0, 1.0, viewport)
  428.         gluPerspective(60.0, float(viewport[2]) / float(viewport[3]), 0, 1)
  429.  
  430.         glMatrixMode(GL_MODELVIEW)
  431.         glLoadIdentity()
  432.         self.transformCamera()
  433.  
  434.         # Draw board
  435.  
  436.         self.transformBoard()
  437.         self.drawSquares()
  438.  
  439.         # Render and check for hits
  440.         # Catch the exception in case we select more than we can fit in the selection buffer
  441.         glFlush()
  442.         try:
  443.             records = glRenderMode(GL_RENDER)
  444.         except GLerror:
  445.             coord = None
  446.         else:
  447.             # Get the first record and use this as the selected square
  448.             if len(records) > 0:
  449.                 (_, _, coord) = records[0]
  450.             else:
  451.                 coord = None
  452.  
  453.         # Reset projection matrix
  454.         glMatrixMode(GL_PROJECTION)
  455.         glLoadIdentity()
  456.         gluPerspective(60.0, float(viewport[2]) / float(viewport[3]), 0.1, 1000)
  457.         
  458.         if coord is None:
  459.             return None
  460.         
  461.         # Convert from co-ordinates to LAN format
  462.         rank = chr(ord('a') + coord[0])
  463.         file = chr(ord('1') + coord[1])
  464.  
  465.         return rank + file
  466.  
  467.     # Private methods
  468.     
  469.     def _coordToLocation(self, coord):
  470.         """
  471.         """
  472.         rank = ord(coord[0]) - ord('a')
  473.         file = ord(coord[1]) - ord('1')
  474.         x = BOARD_BORDER + float(rank) * SQUARE_WIDTH + 0.5 * SQUARE_WIDTH
  475.         z = -(BOARD_BORDER + float(file) * SQUARE_WIDTH + 0.5 * SQUARE_WIDTH)
  476.         
  477.         return (x, 0.0, z)
  478.         
  479.     def animateThrobber(self, timeStep):
  480.         """
  481.         """
  482.         if self.throbberEnabled is False:
  483.             return False
  484.         
  485.         self.throbberAngle += timeStep * (math.pi * 2.0) / 2.0
  486.         while self.throbberAngle > (math.pi * 2.0):
  487.             self.throbberAngle -= 2.0 * math.pi
  488.         return True
  489.         
  490.     def animateRotation(self, timeStep):
  491.         """
  492.         """
  493.         if self.boardAngle == self.targetBoardAngle:
  494.             return False
  495.         
  496.         # Wait unti pieces have stopped moving
  497.         if self.piecesMoving:
  498.             return False
  499.  
  500.         # Rotate board to the chosen angle
  501.         length = abs(self.targetBoardAngle - self.oldBoardAngle)
  502.         self.boardAngle += timeStep * length / BOARD_ROTATION_TIME
  503.         while self.boardAngle > 360.0:
  504.             self.boardAngle -= 360.0
  505.         travelled = self.targetBoardAngle - self.boardAngle
  506.         while travelled < 0.0:
  507.             travelled += 360.0
  508.             
  509.         # If have moved through the remaining angle then clip to the target
  510.         if travelled >= length:
  511.             self.oldBoardAngle = self.boardAngle = self.targetBoardAngle
  512.  
  513.         return True
  514.     
  515.     def animatePieces(self, timeStep):
  516.         """
  517.         """
  518.         if len(self._animationQueue) == 0:
  519.             return False
  520.         
  521.         redraw = False
  522.         animationQueue = []
  523.         for piece in self._animationQueue:
  524.             if piece.animate(timeStep):
  525.                 piece.moving = True
  526.                 redraw = True
  527.                 assert(animationQueue.count(piece) == 0)
  528.                 animationQueue.append(piece)
  529.             else:
  530.                 # Redraw static scene once pieces stop
  531.                 if piece.moving:
  532.                     redraw = True
  533.                     self.redrawStatic = True
  534.                 piece.moving = False
  535.  
  536.                 # Notify higher classes
  537.                 piece.feedback.onMoved()
  538.  
  539.         self._animationQueue = animationQueue
  540.  
  541.         if redraw:
  542.             self.feedback.onRedraw()
  543.  
  544.         return len(self._animationQueue) > 0
  545.             
  546.     def drawThrobber(self):
  547.         """
  548.         """
  549.         glDisable(GL_LIGHTING)
  550.         glDisable(GL_DEPTH_TEST)
  551.             
  552.         # Orthographic projection with even scaling
  553.         glMatrixMode(GL_PROJECTION)
  554.         glLoadIdentity()
  555.             
  556.         if self.viewportWidth > self.viewportHeight:
  557.             h = 1.0
  558.             w = 1.0 * self.viewportWidth / self.viewportHeight
  559.         else:
  560.             h = 1.0 * self.viewportHeight / self.viewportWidth
  561.             w = 1.0
  562.         gluOrtho2D(0, w, 0, h)
  563.             
  564.         glMatrixMode(GL_MODELVIEW)
  565.         glLoadIdentity()
  566.             
  567.         glEnable(GL_BLEND)
  568.         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  569.         glColor4f(0.0, 0.0, 0.0, 0.75)
  570.         glBegin(GL_QUADS)
  571.         glVertex2f(-1.0, -1.0)
  572.         glVertex2f(w + 1.0, -1.0)
  573.         glVertex2f(w + 1.0, h + 1.0)
  574.         glVertex2f(-1.0, h + 1.0)
  575.         glEnd()
  576.             
  577.         NSECTIONS = 9
  578.         RADIUS_OUT0 = 0.4
  579.         RADIUS_OUT1 = 0.43
  580.         RADIUS_OUT2 = 0.4
  581.         RADIUS_IN0 = 0.25#0.1
  582.         RADIUS_IN1 = 0.24#0.09
  583.         RADIUS_IN2 = 0.25#0.1
  584.         STEP_ANGLE = 2.0 * math.pi / float(NSECTIONS)
  585.         HALF_WIDTH = 0.8 * (0.5 * STEP_ANGLE)
  586.  
  587.         glTranslatef(0.5 * w, 0.5 * h, 0.0)
  588.         glBegin(GL_QUADS)
  589.             
  590.         for i in xrange(NSECTIONS):
  591.             theta = 2.0 * math.pi * float(i) / float(NSECTIONS)
  592.             leadTheta = theta + HALF_WIDTH
  593.             lagTheta = theta - HALF_WIDTH
  594.             x0 = math.sin(leadTheta)
  595.             y0 = math.cos(leadTheta)
  596.             x1 = math.sin(theta)
  597.             y1 = math.cos(theta)
  598.             x2 = math.sin(lagTheta)
  599.             y2 = math.cos(lagTheta)
  600.                 
  601.             angleDifference = self.throbberAngle - theta
  602.             if angleDifference > math.pi:
  603.                 angleDifference -= 2.0 * math.pi
  604.             elif angleDifference < -math.pi:
  605.                 angleDifference += 2.0 * math.pi
  606.  
  607.             stepDifference = angleDifference / STEP_ANGLE
  608.             if stepDifference > -0.5 and stepDifference < 0.5:
  609.                 x = 2.0 * abs(stepDifference)
  610.                 glColor4f(1.0, x, x, 0.6)
  611.             else:
  612.                 glColor4f(1.0, 1.0, 1.0, 0.6)
  613.                 
  614.             glVertex2f(RADIUS_IN0 * x0, RADIUS_IN0 * y0)
  615.             glVertex2f(RADIUS_OUT0 * x0, RADIUS_OUT0 * y0)
  616.             glVertex2f(RADIUS_OUT1 * x1, RADIUS_OUT1 * y1)
  617.             glVertex2f(RADIUS_IN1 * x1, RADIUS_IN1 * y1)
  618.                 
  619.             glVertex2f(RADIUS_IN1 * x1, RADIUS_IN1 * y1)
  620.             glVertex2f(RADIUS_OUT1 * x1, RADIUS_OUT1 * y1)
  621.             glVertex2f(RADIUS_OUT2 * x2, RADIUS_OUT2 * y2)
  622.             glVertex2f(RADIUS_IN2 * x2, RADIUS_IN2 * y2)
  623.  
  624.         glEnd()
  625.             
  626.         glDisable(GL_BLEND)
  627.  
  628.     def transformCamera(self):
  629.         """Perform the camera matrix transformation"""
  630.         gluLookAt(0.0, 90.0, 45.0,
  631.                   0.0,  0.0, 5.0,
  632.                   0.0,  1.0,  0.0)
  633.                   
  634.     def _makeNumberingTexture(self):
  635.         WIDTH = 64
  636.         HEIGHT = 64
  637.         TEXTURE_WIDTH = WIDTH*16
  638.         TEXTURE_HEIGHT = HEIGHT
  639.  
  640.         surface = cairo.ImageSurface(cairo.FORMAT_A8, TEXTURE_WIDTH, TEXTURE_HEIGHT)
  641.         context = cairo.Context(surface)
  642.         context.set_source_rgba(1.0, 1.0, 1.0, 1.0)
  643.         context.select_font_face("sans-serif", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
  644.         context.set_font_size(WIDTH)
  645.         (ascent, descent, x, x, x) = context.font_extents()
  646.         scale = WIDTH / (ascent + descent)
  647.  
  648.         def drawCenteredText(x, y, scale, text):
  649.             (w2, h2, w, h, _, _) = context.text_extents(text)
  650.             matrix = context.get_matrix()
  651.             context.translate(x, y)
  652.             context.move_to(-w*scale/2, h*scale/2)
  653.             context.scale(scale, scale)
  654.             context.show_text(text)
  655.             context.set_matrix(matrix)
  656.  
  657.         yoffset = HEIGHT * 0.5
  658.         xoffset = WIDTH * 0.5
  659.         for i in xrange(8):
  660.             f = 'abcdefgh'[i]
  661.             r = '12345678'[i]
  662.             drawCenteredText(xoffset, yoffset, scale, glchess.chess.translate_file(f))
  663.             drawCenteredText(xoffset + (WIDTH * 8), yoffset, scale, glchess.chess.translate_rank(r))
  664.             xoffset += WIDTH
  665.  
  666.         t = glGenTextures(1)
  667.         glBindTexture(GL_TEXTURE_2D, t)
  668.         glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
  669.         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
  670.         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
  671.         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
  672.         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
  673.  
  674.         data = surface.get_data()        
  675.         try:
  676.             gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, TEXTURE_WIDTH, TEXTURE_HEIGHT,
  677.                               GL_ALPHA, GL_UNSIGNED_BYTE, str(data))
  678.         except GLUerror, e:
  679.             glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_ALPHA, GL_UNSIGNED_BYTE, str(data))
  680.             
  681.         return t
  682.     
  683.     def drawNumbering(self):
  684.         if self.numberingTexture is None:
  685.             self.numberingTexture = self._makeNumberingTexture()
  686.             
  687.         TEXT_WIDTH = BOARD_BORDER * 0.8
  688.         TEXT_OFFSET = (BOARD_BORDER + BOARD_CHAMFER) * 0.5
  689.         offset = BOARD_BORDER + SQUARE_WIDTH * 0.5
  690.         whiteZOffset = -TEXT_OFFSET
  691.         blackZOffset = -BOARD_OUTER_WIDTH + TEXT_OFFSET
  692.         leftOffset = TEXT_OFFSET
  693.         rightOffset = BOARD_OUTER_WIDTH - TEXT_OFFSET
  694.  
  695.         def drawLabel(x, z, cell):
  696.             w = 1.0 / 16
  697.             l = cell / 16.0
  698.             
  699.             glPushMatrix()
  700.             glTranslatef(x, 0.0, z)
  701.             glRotatef(-self.boardAngle, 0.0, 1.0, 0.0)
  702.  
  703.             glBegin(GL_QUADS)            
  704.             glTexCoord2f(l, 0.0)
  705.             glVertex3f(-TEXT_WIDTH/2, 0.0, -TEXT_WIDTH/2)
  706.             glTexCoord2f(l, 1.0)
  707.             glVertex3f(-TEXT_WIDTH/2, 0.0, TEXT_WIDTH/2)
  708.             glTexCoord2f(l + w, 1.0)
  709.             glVertex3f(TEXT_WIDTH/2, 0.0, TEXT_WIDTH/2)
  710.             glTexCoord2f(l + w, 0.0)
  711.             glVertex3f(TEXT_WIDTH/2, 0.0, -TEXT_WIDTH/2)
  712.             glEnd()
  713.             
  714.             glPopMatrix()
  715.  
  716.         glNormal3f(0.0, 1.0, 0.0)
  717.         glDisable(GL_DEPTH_TEST)
  718.         glEnable(GL_TEXTURE_2D)
  719.         glEnable(GL_BLEND)
  720.         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  721.         glBindTexture(GL_TEXTURE_2D, self.numberingTexture)
  722.  
  723.         for i in xrange(8):
  724.             drawLabel(leftOffset, -offset, i + 8)
  725.             drawLabel(rightOffset, -offset, i + 8)
  726.             drawLabel(offset, whiteZOffset, i)
  727.             drawLabel(offset, blackZOffset, i)
  728.  
  729.             offset += SQUARE_WIDTH
  730.  
  731.         glEnable(GL_DEPTH_TEST)
  732.         glDisable(GL_BLEND)
  733.         glDisable(GL_TEXTURE_2D)
  734.  
  735.     def drawBoard(self):
  736.         """Draw a chessboard"""
  737.         # Use pre-rendered version if available
  738.         if self.regenerateBoard is False and self.boardList is not None:
  739.             glCallList(self.boardList)
  740.             return
  741.         
  742.         # Attempt to store the board as a display list
  743.         if self.boardList is None:
  744.             list = glGenLists(1)
  745.             if list != 0:
  746.                 self.boardList = list
  747.  
  748.         # If have a list store there
  749.         if self.boardList is not None:
  750.             glNewList(self.boardList, GL_COMPILE)
  751.             
  752.         # Board verticies
  753.         # (lower 12-15 are under 8-11)
  754.         #
  755.         # a b c         d e f
  756.         #
  757.         # 8-----------------9  g
  758.         # |\               /|
  759.         # | 4-------------5 |  h
  760.         # | |             | |
  761.         # | | 0---------1 | |  i
  762.         # | | |         | | |
  763.         # | | |         | | |
  764.         # | | 3---------2 | |  j
  765.         # | |             | |
  766.         # | 7-------------6 |  k
  767.         # |/               \|
  768.         # 11---------------10  l
  769.         #
  770.         #     |- board -|
  771.         #        width
  772.  
  773.         # Draw the border
  774.         glColor3f(*BORDER_COLOUR)
  775.  
  776.         # Top
  777.         a = 0.0
  778.         b = BOARD_CHAMFER
  779.         c = BOARD_BORDER
  780.         d = c + (SQUARE_WIDTH * 8.0)
  781.         e = d + BOARD_BORDER - BOARD_CHAMFER
  782.         f = d + BOARD_BORDER
  783.         l = 0.0
  784.         k = -BOARD_CHAMFER
  785.         j = -BOARD_BORDER
  786.         i = j - (SQUARE_WIDTH * 8.0)
  787.         h = i - BOARD_BORDER + BOARD_CHAMFER
  788.         g = i - BOARD_BORDER
  789.         verticies = [(c, 0.0, i), (d, 0.0, i),
  790.                      (d, 0.0, j), (c, 0.0, j),
  791.                      (b, 0.0, h), (e, 0.0, h),
  792.                      (e, 0.0, k), (b, 0.0, k),
  793.                      (a, -BOARD_CHAMFER, g), (f, -BOARD_CHAMFER, g),
  794.                      (f, -BOARD_CHAMFER, l), (a, -BOARD_CHAMFER, l),
  795.                      (a, -BOARD_DEPTH, g), (f, -BOARD_DEPTH, g), (f, -BOARD_DEPTH, l), (a, -BOARD_DEPTH, l)]
  796.         
  797.         normals = [(0.0, 1.0, 0.0), (0.0, 0.0, -1.0), (1.0, 0.0, 0.0), (0.0, 0.0, 1.0), (-1.0, 0.0, 0.0),
  798.                    (0.0, 0.707, -0.707), (0.707, 0.707, 0.0), (0.0, 0.707, 0.707), (-0.707, 0.707, 0.0)]
  799.         
  800.         #textureCoords = [(0.0, 0.0), (
  801.         
  802.         quads = [(0, 1, 5, 4, 0), (1, 2, 6, 5, 0), (2, 3, 7, 6, 0), (3, 0, 4, 7, 0),
  803.                  (4, 5, 9, 8, 5), (5, 6, 10, 9, 6), (6, 7, 11, 10, 7), (7, 4, 8, 11, 8),
  804.                  (8, 9, 13, 12, 1), (9, 10, 14, 13, 2), (10, 11, 15, 14, 3), (11, 8, 12, 15, 4)]
  805.         
  806.         glDisable(GL_TEXTURE_2D)
  807.         glBegin(GL_QUADS)
  808.         for q in quads:
  809.             glNormal3fv(normals[q[4]])
  810.             #glTexCoord2fv(textureCoords[q[0]])
  811.             glVertex3fv(verticies[q[0]])
  812.             #glTexCoord2fv(textureCoords[q[1]])
  813.             glVertex3fv(verticies[q[1]])
  814.             #glTexCoord2fv(textureCoords[q[2]])
  815.             glVertex3fv(verticies[q[2]])
  816.             #glTexCoord2fv(textureCoords[q[3]])
  817.             glVertex3fv(verticies[q[3]])
  818.         glEnd()
  819.  
  820.         # Draw the squares
  821.         glEnable(GL_TEXTURE_2D)
  822.         for x in [0, 1, 2, 3, 4, 5, 6, 7]:
  823.             for y in [0, 1, 2, 3, 4, 5, 6, 7]:
  824.                 isBlack = (x + (y % 2) + 1) % 2
  825.  
  826.                 # Get the highlight type
  827.                 coord = chr(ord('a') + x) + chr(ord('1') + y)
  828.                 try:
  829.                     highlight = self.highlights[coord]
  830.                 except KeyError:
  831.                     highlight = None
  832.                 
  833.                 if isBlack:
  834.                     colour = BLACK_SQUARE_COLOURS[highlight]
  835.                     self.whiteTexture.bind() #blackTexture
  836.                 else:
  837.                     colour = WHITE_SQUARE_COLOURS[highlight]
  838.                     self.whiteTexture.bind()
  839.  
  840.                 x0 = BOARD_BORDER + (x * SQUARE_WIDTH)
  841.                 x1 = x0 + SQUARE_WIDTH
  842.                 z0 = BOARD_BORDER + (y * SQUARE_WIDTH)
  843.                 z1 = z0 + SQUARE_WIDTH
  844.  
  845.                 glBegin(GL_QUADS)
  846.                 glNormal3f(0.0, 1.0, 0.0)
  847.                 glColor3fv(colour)
  848.                 glTexCoord2f(0.0, 0.0)
  849.                 glVertex3f(x0, 0.0, -z0)
  850.                 glTexCoord2f(1.0, 0.0)
  851.                 glVertex3f(x1, 0.0, -z0)
  852.                 glTexCoord2f(1.0, 1.0)
  853.                 glVertex3f(x1, 0.0, -z1)
  854.                 glTexCoord2f(0.0, 1.0)
  855.                 glVertex3f(x0, 0.0, -z1)
  856.                 glEnd()
  857.         
  858.         if self.boardList is not None:
  859.             glEndList()
  860.             glCallList(self.boardList)
  861.         
  862.     def drawSquares(self):
  863.         """Draw the board squares for picking"""
  864.         
  865.         # draw the floor squares
  866.         for u in [0, 1, 2, 3, 4, 5, 6, 7]:
  867.             glPushName(u)
  868.  
  869.             for v in [0, 1, 2, 3, 4, 5, 6, 7]:
  870.                 glPushName(v)
  871.  
  872.                 # Draw square
  873.                 glBegin(GL_QUADS)
  874.                 x0 = BOARD_BORDER + (u * SQUARE_WIDTH)
  875.                 x1 = x0 + SQUARE_WIDTH
  876.                 z0 = BOARD_BORDER + (v * SQUARE_WIDTH)
  877.                 z1 = z0 + SQUARE_WIDTH
  878.  
  879.                 glVertex3f(x0, 0.0, -z0)
  880.                 glVertex3f(x1, 0.0, -z0)
  881.                 glVertex3f(x1, 0.0, -z1)
  882.                 glVertex3f(x0, 0.0, -z1)
  883.                 glEnd()
  884.  
  885.                 glPopName()
  886.             glPopName()
  887.         
  888.     def drawPieces(self):
  889.         """Draw the pieces in the scene"""
  890.         glEnable(GL_TEXTURE_2D)
  891.  
  892.         for piece in self.pieces:
  893.             glPushMatrix()
  894.             glTranslatef(piece.pos[0], piece.pos[1], piece.pos[2])
  895.  
  896.             # Draw the model
  897.             piece.draw()
  898.  
  899.             glPopMatrix()
  900.         
  901.         glDisable(GL_TEXTURE_2D)
  902.  
  903.     def transformBoard(self):
  904.         """Perform the board transform"""
  905.         # Rotate the board
  906.         glRotatef(self.boardAngle, 0.0, 1.0, 0.0)
  907.  
  908.         # Offset board so centre is (0.0,0.0)
  909.         glTranslatef(-OFFSET, 0.0, OFFSET)
  910.