Package VisionEgg :: Module Core
[frames] | no frames]

Source Code for Module VisionEgg.Core

   1  # The Vision Egg: Core 
   2  # 
   3  # Copyright (C) 2001-2004 Andrew Straw 
   4  # Copyright (C) 2004-2008 California Institute of Technology 
   5  # 
   6  # URL: <http://www.visionegg.org/> 
   7  # 
   8  # Distributed under the terms of the GNU Lesser General Public License 
   9  # (LGPL). See LICENSE.TXT that came with this file. 
  10   
  11  """ 
  12  Core Vision Egg functionality. 
  13   
  14  This module contains the architectural foundations of the Vision Egg. 
  15   
  16  """ 
  17   
  18  #################################################################### 
  19  # 
  20  #        Import all the necessary packages 
  21  # 
  22  #################################################################### 
  23   
  24  import sys, types, math, time, os               # standard Python modules 
  25  import StringIO 
  26   
  27  import logging                                  # available in Python 2.3 
  28   
  29  import VisionEgg                                # Vision Egg base module (__init__.py) 
  30  import VisionEgg.PlatformDependent              # platform dependent Vision Egg C code 
  31  import VisionEgg.ParameterTypes as ve_types     # Vision Egg type checking 
  32  import VisionEgg.GLTrace                        # Allows tracing of all OpenGL calls 
  33  import VisionEgg.ThreeDeeMath                   # OpenGL math simulation 
  34   
  35  import pygame                                   # pygame handles OpenGL window setup 
  36  import pygame.locals 
  37  import pygame.display 
  38   
  39  import VisionEgg.GL as gl # get all OpenGL stuff in one namespace 
  40   
  41  import numpy 
  42  import numpy.oldnumeric as Numeric # emulate old Numeric Python package 
  43   
  44  # Define "sum" if it's not available as Python function 
  45  try: 
  46      sum 
  47  except NameError: 
  48      import operator 
49 - def sum( values ):
50 return reduce(operator.add, values )
51
52 -def swap_buffers():
53 VisionEgg.config._FRAMECOUNT_ABSOLUTE += 1 54 return pygame.display.flip()
55 56 #################################################################### 57 # 58 # Screen 59 # 60 #################################################################### 61
62 -class Screen(VisionEgg.ClassWithParameters):
63 """An OpenGL window, possibly displayed across multiple displays. 64 65 A Screen instance is an OpenGL window for the Vision Egg to draw 66 in. For an instance of Screen to do anything useful, it must 67 contain one or more instances of the Viewport class and one or 68 more instances of the Stimulus class. 69 70 Currently, only one OpenGL window is supported by the library with 71 which the Vision Egg initializes graphics (pygame/SDL). However, 72 this need not limit display to a single physical display device. 73 Many video drivers, for example, allow applications to treat two 74 separate monitors as one large array of contiguous pixels. By 75 sizing a window such that it occupies both monitors and creating 76 separate viewports for the portion of the window on each monitor, 77 a multiple screen effect can be created. 78 79 Public read-only variables 80 ========================== 81 size -- Tuple of 2 integers specifying width and height 82 83 Parameters 84 ========== 85 bgcolor -- background color (AnyOf(Sequence3 of Real or Sequence4 of Real)) 86 Default: (0.5, 0.5, 0.5, 0.0) 87 88 Constant Parameters 89 =================== 90 alpha_bits -- number of bits per pixel for alpha channel. Can be set with VISIONEGG_REQUEST_ALPHA_BITS (UnsignedInteger) 91 Default: (determined at runtime) 92 blue_bits -- number of bits per pixel for blue channel. Can be set with VISIONEGG_REQUEST_BLUE_BITS (UnsignedInteger) 93 Default: (determined at runtime) 94 double_buffer -- use double buffering? Can be set with VISIONEGG_DOUBLE_BUFFER (Boolean) 95 Default: (determined at runtime) 96 frameless -- remove standard window frame? Can be set with VISIONEGG_FRAMELESS_WINDOW (Boolean) 97 Default: (determined at runtime) 98 fullscreen -- use full screen? Can be set with VISIONEGG_FULLSCREEN (Boolean) 99 Default: (determined at runtime) 100 green_bits -- number of bits per pixel for green channel. Can be set with VISIONEGG_REQUEST_GREEN_BITS (UnsignedInteger) 101 Default: (determined at runtime) 102 hide_mouse -- hide the mouse cursor? Can be set with VISIONEGG_HIDE_MOUSE (Boolean) 103 Default: (determined at runtime) 104 is_stereo -- allocate stereo framebuffers? Can be set with VISIONEGG_REQUEST_STEREO (Boolean) 105 Default: (determined at runtime) 106 maxpriority -- raise priority? (platform dependent) Can be set with VISIONEGG_MAXPRIORITY (Boolean) 107 Default: (determined at runtime) 108 multisample_samples -- preferred number of multisamples for FSAA (UnsignedInteger) 109 Default: (determined at runtime) 110 preferred_bpp -- preferred bits per pixel (bit depth) Can be set with VISIONEGG_PREFERRED_BPP (UnsignedInteger) 111 Default: (determined at runtime) 112 red_bits -- number of bits per pixel for red channel. Can be set with VISIONEGG_REQUEST_RED_BITS (UnsignedInteger) 113 Default: (determined at runtime) 114 size -- size (units: pixels) Can be set with VISIONEGG_SCREEN_W and VISIONEGG_SCREEN_H (Sequence2 of Real) 115 Default: (determined at runtime) 116 sync_swap -- synchronize buffer swaps to vertical sync? Can be set with VISIONEGG_SYNC_SWAP (Boolean) 117 Default: (determined at runtime) 118 """ 119 120 parameters_and_defaults = VisionEgg.ParameterDefinition({ 121 'bgcolor':((0.5,0.5,0.5,0.0), 122 ve_types.AnyOf(ve_types.Sequence3(ve_types.Real), 123 ve_types.Sequence4(ve_types.Real)), 124 'background color',), 125 }) 126 127 constant_parameters_and_defaults = VisionEgg.ParameterDefinition({ 128 'size':(None, 129 ve_types.Sequence2(ve_types.Real), 130 'size (units: pixels) Can be set with VISIONEGG_SCREEN_W and VISIONEGG_SCREEN_H'), 131 'fullscreen':(None, 132 ve_types.Boolean, 133 'use full screen? Can be set with VISIONEGG_FULLSCREEN'), 134 'double_buffer':(None, 135 ve_types.Boolean, 136 'use double buffering? Can be set with VISIONEGG_DOUBLE_BUFFER'), 137 'preferred_bpp':(None, 138 ve_types.UnsignedInteger, 139 'preferred bits per pixel (bit depth) Can be set with VISIONEGG_PREFERRED_BPP'), 140 'maxpriority':(None, 141 ve_types.Boolean, 142 'raise priority? (platform dependent) Can be set with VISIONEGG_MAXPRIORITY'), 143 'hide_mouse':(None, 144 ve_types.Boolean, 145 'hide the mouse cursor? Can be set with VISIONEGG_HIDE_MOUSE'), 146 'frameless':(None, 147 ve_types.Boolean, 148 'remove standard window frame? Can be set with VISIONEGG_FRAMELESS_WINDOW'), 149 'sync_swap':(None, 150 ve_types.Boolean, 151 'synchronize buffer swaps to vertical sync? Can be set with VISIONEGG_SYNC_SWAP'), 152 'red_bits':(None, 153 ve_types.UnsignedInteger, 154 'number of bits per pixel for red channel. Can be set with VISIONEGG_REQUEST_RED_BITS'), 155 'green_bits':(None, 156 ve_types.UnsignedInteger, 157 'number of bits per pixel for green channel. Can be set with VISIONEGG_REQUEST_GREEN_BITS'), 158 'blue_bits':(None, 159 ve_types.UnsignedInteger, 160 'number of bits per pixel for blue channel. Can be set with VISIONEGG_REQUEST_BLUE_BITS'), 161 'alpha_bits':(None, 162 ve_types.UnsignedInteger, 163 'number of bits per pixel for alpha channel. Can be set with VISIONEGG_REQUEST_ALPHA_BITS'), 164 'is_stereo':(None, 165 ve_types.Boolean, 166 'allocate stereo framebuffers? Can be set with VISIONEGG_REQUEST_STEREO'), 167 'multisample_samples':(None, # support added by Mark Halko 168 ve_types.UnsignedInteger, 169 'preferred number of multisamples for FSAA'), 170 }) 171 172 __slots__ = ( 173 '__cursor_visible_func__', 174 '__pygame_quit__', 175 '_put_pixels_texture_stimulus', 176 '_pixel_coord_projection', 177 ) 178
179 - def __init__(self,**kw):
180 logger = logging.getLogger('VisionEgg.Core') 181 182 VisionEgg.ClassWithParameters.__init__(self,**kw) 183 184 cp = self.constant_parameters # shorthand 185 if cp.size is None: 186 cp.size = (VisionEgg.config.VISIONEGG_SCREEN_W, 187 VisionEgg.config.VISIONEGG_SCREEN_H) 188 if cp.double_buffer is None: 189 cp.double_buffer = VisionEgg.config.VISIONEGG_DOUBLE_BUFFER 190 if cp.fullscreen is None: 191 cp.fullscreen = VisionEgg.config.VISIONEGG_FULLSCREEN 192 if cp.preferred_bpp is None: 193 cp.preferred_bpp = VisionEgg.config.VISIONEGG_PREFERRED_BPP 194 if cp.maxpriority is None: 195 cp.maxpriority = VisionEgg.config.VISIONEGG_MAXPRIORITY 196 if cp.hide_mouse is None: 197 cp.hide_mouse = VisionEgg.config.VISIONEGG_HIDE_MOUSE 198 if cp.frameless is None: 199 cp.frameless = VisionEgg.config.VISIONEGG_FRAMELESS_WINDOW 200 if cp.sync_swap is None: 201 cp.sync_swap = VisionEgg.config.VISIONEGG_SYNC_SWAP 202 if cp.red_bits is None: 203 cp.red_bits = VisionEgg.config.VISIONEGG_REQUEST_RED_BITS 204 if cp.green_bits is None: 205 cp.green_bits = VisionEgg.config.VISIONEGG_REQUEST_GREEN_BITS 206 if cp.blue_bits is None: 207 cp.blue_bits = VisionEgg.config.VISIONEGG_REQUEST_BLUE_BITS 208 if cp.alpha_bits is None: 209 cp.alpha_bits = VisionEgg.config.VISIONEGG_REQUEST_ALPHA_BITS 210 if cp.is_stereo is None: 211 cp.is_stereo = VisionEgg.config.VISIONEGG_REQUEST_STEREO 212 if cp.multisample_samples is None: 213 cp.multisample_samples = VisionEgg.config.VISIONEGG_MULTISAMPLE_SAMPLES 214 215 if VisionEgg.config.SYNCLYNC_PRESENT: 216 global synclync # import into global namespace 217 import synclync 218 try: 219 VisionEgg.config._SYNCLYNC_CONNECTION = synclync.SyncLyncConnection() 220 except synclync.SyncLyncError, x: 221 logger.warning( "Could not connect to SyncLync device (SyncLyncError: %s)."%str(x)) 222 VisionEgg.config._SYNCLYNC_CONNECTION = None 223 else: 224 logger.info( "Connected to SyncLync device" ) 225 else: 226 VisionEgg.config._SYNCLYNC_CONNECTION = None 227 228 # Attempt to synchronize buffer swapping with vertical sync 229 if cp.sync_swap: 230 sync_success = VisionEgg.PlatformDependent.sync_swap_with_vbl_pre_gl_init() 231 232 # Initialize pygame stuff 233 # if sys.platform == "darwin": # bug in Mac OS X version of pygame 234 # pygame.init() 235 pygame.display.init() 236 237 if hasattr(pygame.display,"gl_set_attribute"): 238 pygame.display.gl_set_attribute(pygame.locals.GL_RED_SIZE,cp.red_bits) 239 pygame.display.gl_set_attribute(pygame.locals.GL_GREEN_SIZE,cp.green_bits) 240 pygame.display.gl_set_attribute(pygame.locals.GL_BLUE_SIZE,cp.blue_bits) 241 pygame.display.gl_set_attribute(pygame.locals.GL_ALPHA_SIZE,cp.alpha_bits) 242 pygame.display.gl_set_attribute(pygame.locals.GL_STEREO,cp.is_stereo) 243 #Request FSAA 244 if cp.multisample_samples > 0 : 245 pygame.display.gl_set_attribute(pygame.locals.GL_MULTISAMPLEBUFFERS,1) 246 pygame.display.gl_set_attribute(pygame.locals.GL_MULTISAMPLESAMPLES,cp.multisample_samples) 247 else: 248 logger.debug("Could not request or query exact bit depths, " 249 "alpha or stereo because you need " 250 "pygame release 1.4.9 or greater. This is " 251 "only of concern if you use a stimulus that " 252 "needs this. In that case, the stimulus " 253 "should check for the desired feature(s).") 254 255 if not hasattr(pygame.display,"set_gamma_ramp"): 256 logger.debug("set_gamma_ramp function not available " 257 "because you need pygame release 1.5 or " 258 "greater. This is only of concern if you " 259 "need this feature.") 260 pygame.display.set_caption("Vision Egg") 261 262 flags = pygame.locals.OPENGL 263 if cp.double_buffer: 264 flags = flags | pygame.locals.DOUBLEBUF 265 if cp.fullscreen: 266 flags = flags | pygame.locals.FULLSCREEN 267 if cp.frameless: 268 flags = flags | pygame.locals.NOFRAME 269 270 try_bpp = cp.preferred_bpp 271 272 append_str = "" 273 if cp.fullscreen: 274 screen_mode = "fullscreen" 275 else: 276 screen_mode = "window" 277 if hasattr(pygame.display,"gl_set_attribute"): 278 append_str = " (%d %d %d %d RGBA)."%(cp.red_bits, 279 cp.green_bits, 280 cp.blue_bits, 281 cp.alpha_bits) 282 283 logger.info("Requesting %s %d x %d %d bpp%s"% 284 (screen_mode,self.size[0],self.size[1], 285 try_bpp,append_str)) 286 287 pygame.display.set_mode(self.size, flags, try_bpp ) 288 # set a global variable so we know workaround avoid pygame bug 289 VisionEgg.config._pygame_started = 1 290 291 try: 292 if sys.platform != 'darwin': 293 pygame.display.set_icon(pygame.transform.scale(pygame.image.load( 294 os.path.join(VisionEgg.config.VISIONEGG_SYSTEM_DIR, 295 'data','visionegg.bmp')).convert(),(32,32))) 296 else: 297 import AppKit # requires PyObjC, which is required by pygame osx 298 im = AppKit.NSImage.alloc() 299 im.initWithContentsOfFile_( 300 os.path.join(VisionEgg.config.VISIONEGG_SYSTEM_DIR, 301 'data','visionegg.tif')) 302 AppKit.NSApplication.setApplicationIconImage_(AppKit.NSApp(),im) 303 304 except Exception,x: 305 logger.info("Error while trying to set_icon: %s: %s"% 306 (str(x.__class__),str(x))) 307 308 global gl_vendor, gl_renderer, gl_version 309 gl_vendor = gl.glGetString(gl.GL_VENDOR) 310 gl_renderer = gl.glGetString(gl.GL_RENDERER) 311 gl_version = gl.glGetString(gl.GL_VERSION) 312 313 logger.info("OpenGL %s, %s, %s (PyOpenGL %s)"% 314 (gl_version, gl_renderer, gl_vendor, gl.__version__)) 315 316 if gl_renderer == "GDI Generic" and gl_vendor == "Microsoft Corporation": 317 logger.warning("Using default Microsoft Windows OpenGL " 318 "drivers. Please (re-)install the latest " 319 "video drivers from your video card " 320 "manufacturer to get hardware accelerated " 321 "performance.") 322 if gl_renderer == "Mesa GLX Indirect" and gl_vendor == "VA Linux Systems, Inc.": 323 logger.warning("Using default Mesa GLX drivers. Please " 324 "(re-)install the latest video drivers from " 325 "your video card manufacturer or DRI " 326 "project to get hardware accelarated " 327 "performance.") 328 # Set values to unknown and fill based on OpenGL values 329 cp.red_bits = None 330 cp.green_bits = None 331 cp.blue_bits = None 332 cp.alpha_bits = None 333 cp.is_stereo = None 334 got_bpp = pygame.display.Info().bitsize 335 append_str = '' 336 if hasattr(pygame.display,"gl_get_attribute"): 337 # Fill in values as known 338 cp.red_bits = pygame.display.gl_get_attribute(pygame.locals.GL_RED_SIZE) 339 cp.green_bits = pygame.display.gl_get_attribute(pygame.locals.GL_GREEN_SIZE) 340 cp.blue_bits = pygame.display.gl_get_attribute(pygame.locals.GL_BLUE_SIZE) 341 cp.alpha_bits = pygame.display.gl_get_attribute(pygame.locals.GL_ALPHA_SIZE) 342 cp.is_stereo = pygame.display.gl_get_attribute(pygame.locals.GL_STEREO) 343 if cp.is_stereo: stereo_string = ' stereo' 344 else: stereo_string = '' 345 append_str = " (%d %d %d %d RGBA%s)"%( 346 cp.red_bits,cp.green_bits,cp.blue_bits,cp.alpha_bits, 347 stereo_string) 348 logger.info("Video system reports %d bpp%s."%(got_bpp,append_str)) 349 if got_bpp < try_bpp: 350 logger.warning("Video system reports %d bits per pixel, " 351 "while your program requested %d. Can you " 352 "adjust your video drivers?"%(got_bpp, 353 try_bpp)) 354 # Save the address of these functions so they can be called 355 # when closing the screen. 356 self.__cursor_visible_func__ = pygame.mouse.set_visible 357 self.__pygame_quit__ = pygame.quit 358 359 #Check FSAA requests 360 if cp.multisample_samples>0 : 361 if hasattr(pygame.display,"gl_set_attribute"): 362 got_ms_buf = pygame.display.gl_get_attribute(pygame.locals.GL_MULTISAMPLEBUFFERS) 363 got_ms_samp = pygame.display.gl_get_attribute(pygame.locals.GL_MULTISAMPLESAMPLES) 364 if got_ms_samp < cp.multisample_samples : 365 logger.warning("Video system reports %d multisample samples, " 366 "while you requested %d. FSAA requires " 367 "SDL > 1.2.6, check that it is installed."%(got_ms_samp, cp.multisample_samples)) 368 369 # Attempt to synchronize buffer swapping with vertical sync again 370 if cp.sync_swap: 371 if not sync_success: 372 if not VisionEgg.PlatformDependent.sync_swap_with_vbl_post_gl_init(): 373 cp.sync_swap = False 374 logger.warning("Unable to detect or automatically " 375 "synchronize buffer swapping with " 376 "vertical retrace. May be possible " 377 "by manually adjusting video " 378 "drivers. (Look for 'Enable " 379 "Vertical Sync' or similar.) If " 380 "buffer swapping is not " 381 "synchronized, frame by frame " 382 "control will not be possible. " 383 "Because of this, you will probably " 384 "get a warning about calculated " 385 "frames per second different than " 386 "specified.") 387 # Check previously made OpenGL assumptions now that we have OpenGL window 388 post_gl_init() 389 390 if cp.hide_mouse: 391 self.__cursor_visible_func__(0) 392 393 # Attempt to set maximum priority (This may not be the best 394 # place in the code to do it because it's an application-level 395 # thing, not a screen-level thing, but it fits reasonably well 396 # here for now.) 397 if cp.maxpriority: 398 VisionEgg.PlatformDependent.set_priority() # defaults to max priority 399 400 if hasattr(VisionEgg.config,'_open_screens'): 401 VisionEgg.config._open_screens.append(self) 402 else: 403 VisionEgg.config._open_screens = [self]
404 405 # Use Python descriptors (introduced in Python 2.2) to link size 406 # attribute to constant_parameters.size.
407 - def get_size(self): return self.constant_parameters.size
408 - def set_size(self, value): raise RuntimeError("Attempting to set read-only value")
409 size = property(get_size,set_size) 410
411 - def get_framebuffer_as_image(self, 412 buffer='back', 413 format=gl.GL_RGB, 414 position=(0,0), 415 anchor='lowerleft', 416 size=None, # if None, use full screen 417 ):
418 """get pixel values from framebuffer to PIL image""" 419 import Image # Could import this at the beginning of the file, but it breaks sometimes. 420 421 fb_array = self.get_framebuffer_as_array(buffer=buffer, 422 format=format, 423 position=position, 424 anchor=anchor, 425 size=size, 426 ) 427 size = fb_array.shape[1], fb_array.shape[0] 428 if format == gl.GL_RGB: 429 pil_mode = 'RGB' 430 elif format == gl.GL_RGBA: 431 pil_mode = 'RGBA' 432 fb_image = Image.fromstring(pil_mode,size,fb_array.tostring()) 433 fb_image = fb_image.transpose( Image.FLIP_TOP_BOTTOM ) 434 return fb_image
435
436 - def get_framebuffer_as_array(self, 437 buffer='back', 438 format=gl.GL_RGB, 439 position=(0,0), 440 anchor='lowerleft', 441 size=None, # if None, use full screen 442 ):
443 """get pixel values from framebuffer to Numeric array"""# (SLOW)""" 444 if size is None: 445 size = self.size 446 lowerleft = VisionEgg._get_lowerleft(position,anchor,size) 447 if buffer == 'front': 448 gl.glReadBuffer( gl.GL_FRONT ) 449 elif buffer == 'back': 450 gl.glReadBuffer( gl.GL_BACK ) 451 else: 452 raise ValueError('No support for "%s" framebuffer'%buffer) 453 454 # according to Apple's glGrab demo, this should force DMA transfers: 455 gl.glPixelStorei(gl.GL_PACK_ALIGNMENT, 4) 456 gl.glPixelStorei(gl.GL_PACK_ROW_LENGTH, 0) 457 gl.glPixelStorei(gl.GL_PACK_SKIP_ROWS, 0) 458 gl.glPixelStorei(gl.GL_PACK_SKIP_PIXELS, 0) 459 if gl_version >= '1.2' and hasattr(gl,'GL_BGRA'): 460 framebuffer_pixels = gl.glReadPixels(lowerleft[0],lowerleft[1], 461 size[0],size[1], 462 gl.GL_BGRA, 463 gl.GL_UNSIGNED_INT_8_8_8_8_REV) 464 raw_format = 'BGRA' 465 else: 466 framebuffer_pixels = gl.glReadPixels(lowerleft[0],lowerleft[1], 467 size[0],size[1], 468 gl.GL_RGBA, 469 gl.GL_UNSIGNED_BYTE) 470 raw_format = 'RGBA' 471 fb_array = Numeric.fromstring(framebuffer_pixels,Numeric.UInt8) 472 fb_array = Numeric.reshape(fb_array,(size[1],size[0],4)) 473 # These work, but I don't know why. There must be something I 474 # don't understand about byte ordering! 475 if format == gl.GL_RGB: 476 if raw_format == 'BGRA': 477 fb_array = fb_array[:,:,1:] 478 elif raw_format == 'RGBA': 479 fb_array = fb_array[:,:,:3] 480 elif format == gl.GL_RGBA: 481 if raw_format == 'BGRA': 482 alpha = fb_array[:,:,0,Numeric.NewAxis] 483 fb_array = fb_array[:,:,1:] 484 fb_array = Numeric.concatenate( (fb_array,alpha), axis=2) 485 elif raw_format == 'RGBA': 486 pass 487 else: 488 raise NotImplementedError("Only RGB and RGBA formats currently supported") 489 return fb_array
490
491 - def put_pixels(self, 492 pixels=None, 493 position=(0,0), 494 anchor='lowerleft', 495 scale_x=1.0, # "zoom" the pixels 496 scale_y=1.0, # "zoom" the pixels 497 texture_min_filter=gl.GL_NEAREST, # only used if scale < 1.0 498 texture_mag_filter=gl.GL_NEAREST, # only used if scale > 1.0 499 internal_format=gl.GL_RGB, # pixel data converted to this format in texture (gl.GL_RGBA also useful) 500 ):
501 """Put pixel values to screen. 502 503 Pixel values become texture data using the VisionEgg.Textures 504 module. Any source of texture data accepted by that module is 505 accepted here. 506 507 This function could be sped up by allocating a fixed OpenGL texture object. 508 509 """ 510 511 import VisionEgg.Textures # import here to avoid import loop 512 make_new_texture_object = 0 513 if not hasattr(self, "_put_pixels_texture_stimulus"): 514 make_new_texture_object = 1 515 else: 516 if internal_format != self._put_pixels_texture_stimulus.constant_parameters.internal_format: 517 make_new_texture_object = 1 518 if make_new_texture_object: 519 # For speed, don't do this on anything other than 1st run 520 texture = VisionEgg.Textures.Texture(pixels) 521 on_screen_size = (texture.size[0]*scale_x, texture.size[1]*scale_y) 522 t = VisionEgg.Textures.TextureStimulus(texture=texture, 523 position=position, 524 anchor=anchor, 525 size=on_screen_size, 526 mipmaps_enabled=0, 527 texture_min_filter=texture_min_filter, 528 texture_mag_filter=texture_mag_filter, 529 internal_format = internal_format, 530 ) 531 self._put_pixels_texture_stimulus = t # rename 532 self._pixel_coord_projection = OrthographicProjection(left=0, 533 right=self.size[0], 534 bottom=0, 535 top=self.size[1], 536 z_clip_near=0.0, 537 z_clip_far=1.0) 538 else: 539 # We've run once before and therefore already have a 540 # texture stimulus. (XXX In the future, make use of 541 # already assigned texture object and use put_sub_image 542 # for speed.) 543 self._put_pixels_texture_stimulus.parameters.texture = VisionEgg.Textures.Texture(pixels) 544 545 self._pixel_coord_projection.push_and_set_gl_projection() # Save projection 546 self._put_pixels_texture_stimulus.draw() # Draw pixels as texture 547 548 gl.glMatrixMode(gl.GL_PROJECTION) # Restore projection 549 gl.glPopMatrix()
550
551 - def query_refresh_rate(self):
553
554 - def measure_refresh_rate(self,average_over_seconds=0.1):
555 """Measure the refresh rate. Assumes swap buffers synced.""" 556 start_time = VisionEgg.time_func() 557 duration_sec = 0.0 558 num_frames = 0 559 while duration_sec < average_over_seconds: 560 swap_buffers() 561 now = VisionEgg.time_func() 562 num_frames += 1 563 duration_sec = now - start_time 564 if duration_sec > 0.0: 565 fps = num_frames / duration_sec 566 else: 567 fps = 0.0 568 return fps
569
570 - def clear(self):
571 """Called by Presentation instance. Clear the screen.""" 572 573 c = self.parameters.bgcolor # Shorthand 574 if len(c) == 4: 575 gl.glClearColor(*c) 576 else: 577 gl.glClearColor(c[0],c[1],c[2],0.0) # set alpha to 0.0 unless specified 578 gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
579
580 - def make_current(self):
581 """Called by Viewport instance. Makes screen active for drawing. 582 583 Can not be implemented until multiple screens are possible.""" 584 pass
585
586 - def set_gamma_ramp(self,*args,**kw):
587 """Set the gamma_ramp, if supported. 588 589 Call pygame.display.set_gamma_ramp, if available. 590 591 Returns True on success, False otherwise.""" 592 if not hasattr(pygame.display,"set_gamma_ramp"): 593 logger = logging.getLogger('VisionEgg.Core') 594 logger.error("Need pygame 1.5 or greater for set_gamma_ramp function") 595 return False 596 if pygame.display.set_gamma_ramp(*args,**kw): 597 return True 598 else: 599 return False
600
601 - def close(self):
602 """Close the screen. 603 604 You can call this to close the screen. Not necessary during 605 normal operation because it gets automatically deleted.""" 606 # Close pygame if possible 607 if hasattr(VisionEgg.config,'_open_screens'): 608 if self in VisionEgg.config._open_screens: 609 VisionEgg.config._open_screens.remove(self) 610 if len(VisionEgg.config._open_screens) == 0: 611 # no more open screens 612 if hasattr(self,"__cursor_visible_func__"): 613 self.__cursor_visible_func__(1) 614 pygame.quit() 615 # No access to the cursor visible function anymore 616 if hasattr(self,"__cursor_visible_func__"): 617 del self.__cursor_visible_func__
618
619 - def __del__(self):
620 # Make sure mouse is visible after screen closed. 621 if hasattr(self,"__cursor_visible_func__"): 622 try: 623 self.__cursor_visible_func__(1) 624 self.__pygame_quit__() 625 except pygame.error, x: 626 if str(x) != 'video system not initialized': 627 raise
628
629 - def create_default():
630 """Alternative constructor using configuration variables. 631 632 Most of the time you can create and instance of Screen using 633 this method. If your script needs explicit control of the 634 Screen parameters, initialize with the normal constructor. 635 636 Uses VisionEgg.config.VISIONEGG_GUI_INIT to determine how the 637 default screen parameters should are determined. If this 638 value is 0, the values from VisionEgg.cfg are used. If this 639 value is 1, a GUI panel is opened and allows manual settings 640 of the screen parameters. """ 641 642 global VisionEgg # Allow "VisionEgg.config" instead of just "config" 643 if VisionEgg.config.VISIONEGG_GUI_INIT: 644 import VisionEgg.GUI # Could import in beginning, but no need if not using GUI 645 window = VisionEgg.GUI.GraphicsConfigurationWindow() 646 window.mainloop() # All this does is adjust VisionEgg.config 647 if not window.clicked_ok: 648 sys.exit() # User wants to quit 649 screen = None 650 try: 651 screen = Screen(size=(VisionEgg.config.VISIONEGG_SCREEN_W, 652 VisionEgg.config.VISIONEGG_SCREEN_H), 653 fullscreen=VisionEgg.config.VISIONEGG_FULLSCREEN, 654 preferred_bpp=VisionEgg.config.VISIONEGG_PREFERRED_BPP, 655 bgcolor=(0.5,0.5,0.5,0.0), 656 maxpriority=VisionEgg.config.VISIONEGG_MAXPRIORITY, 657 frameless=VisionEgg.config.VISIONEGG_FRAMELESS_WINDOW, 658 hide_mouse=VisionEgg.config.VISIONEGG_HIDE_MOUSE) 659 finally: 660 if screen is None: 661 # Opening screen failed. Let's do any cleanup that Screen.__init__ missed. 662 try: 663 pygame.mouse.set_visible(1) # make sure mouse is visible 664 pygame.quit() # close screen 665 except pygame.error, x: 666 if str(x) != 'video system not initialized': 667 raise 668 669 if screen is None: 670 raise RuntimeError("Screen open failed. Check your error log for a traceback.") 671 672 gamma_source = VisionEgg.config.VISIONEGG_GAMMA_SOURCE.lower() 673 if gamma_source != 'none': 674 if gamma_source == 'invert': 675 native_red = VisionEgg.config.VISIONEGG_GAMMA_INVERT_RED 676 native_green = VisionEgg.config.VISIONEGG_GAMMA_INVERT_GREEN 677 native_blue = VisionEgg.config.VISIONEGG_GAMMA_INVERT_BLUE 678 red = screen._create_inverted_gamma_ramp( native_red ) 679 green = screen._create_inverted_gamma_ramp( native_green ) 680 blue = screen._create_inverted_gamma_ramp( native_blue ) 681 gamma_set_string = "linearized gamma lookup tables to correct "+\ 682 "monitor with native gammas (%f, %f, %f) RGB"%( 683 native_red, 684 native_green, 685 native_blue) 686 elif gamma_source == 'file': 687 filename = VisionEgg.config.VISIONEGG_GAMMA_FILE 688 red, green, blue = screen._open_gamma_file(filename) 689 gamma_set_string = "set gamma lookup tables from data in file %s"%os.path.abspath(filename) 690 else: 691 raise ValueError("Unknown gamma source: '%s'"%gamma_source) 692 logger = logging.getLogger('VisionEgg.Core') 693 if not screen.set_gamma_ramp(red,green,blue): 694 logger.warning( "Setting gamma ramps failed." ) 695 else: 696 logger.info( "Gamma set sucessfully: %s"%gamma_set_string ) 697 return screen
698 create_default = staticmethod(create_default) 699
700 - def _create_inverted_gamma_ramp(self, gamma):
701 # c is a constant scale factor. It is always 1.0 when 702 # luminance is normalized to range [0.0,1.0] and input units 703 # in range [0.0,1.0], as is OpenGL standard. 704 c = 1.0 705 inc = 1.0/255 706 target_luminances = Numeric.arange(0.0,1.0+inc,inc) 707 output_ramp = Numeric.zeros(target_luminances.shape,Numeric.Int) 708 for i in range(len(target_luminances)): 709 L = target_luminances[i] 710 if L == 0.0: 711 v_88fp = 0 712 else: 713 v = math.exp( (math.log(L) - math.log(c)) /gamma) 714 v_88fp = int(round((v*255) * 256)) # convert to from [0.0,1.0] floating point to [0.0,255.0] 8.8 fixed point 715 output_ramp[i] = v_88fp # 8.8 fixed point format 716 return list(output_ramp) # convert to Python list
717
718 - def _open_gamma_file(self, filename):
719 fd = open(filename,"r") 720 gamma_values = [] 721 for line in fd.readlines(): 722 line = line.strip() # remove leading/trailing whitespace 723 if line.startswith("#"): # comment, ignore 724 continue 725 gamma_values.append( map(int, line.split() ) ) 726 if len(gamma_values[-1]) != 3: 727 raise ValueError("expected 3 values per gamma entry") 728 if len(gamma_values) != 256: 729 raise ValueError("expected 256 gamma entries") 730 red, green, blue = zip(*gamma_values) 731 return red,green,blue
732
733 -def get_default_screen():
734 """Make an instance of Screen using a GUI window or from config file.""" 735 return Screen.create_default()
736 737 #################################################################### 738 # 739 # Projection and derived classes 740 # 741 #################################################################### 742
743 -class ProjectionBaseClass(VisionEgg.ClassWithParameters):
744 """Converts stimulus coordinates to viewport coordinates. 745 746 This is an abstract base class which should be subclassed for 747 actual use. 748 749 Parameters 750 ========== 751 matrix -- matrix specifying projection (Sequence4x4 of Real) 752 Default: [[1 0 0 0] 753 [0 1 0 0] 754 [0 0 1 0] 755 [0 0 0 1]] 756 """ 757 758 # WARNING: This implementation should really get cleaned up and 759 # NOT use OpenGL except when purposefully setting matrices. 760 761 parameters_and_defaults = VisionEgg.ParameterDefinition({ 762 'matrix':( Numeric.identity(4), # 4x4 identity matrix 763 ve_types.Sequence4x4(ve_types.Real), 764 'matrix specifying projection'), 765 }) 766 767 __slots__ = ( 768 'projection_type', 769 ) 770
771 - def __init__(self,**kw):
772 VisionEgg.ClassWithParameters.__init__(self,**kw) 773 self.projection_type = None # derived class must override
774
775 - def _get_matrix_type(self):
776 if self.projection_type == gl.GL_PROJECTION: 777 return gl.GL_PROJECTION_MATRIX 778 elif self.projection_type == gl.GL_MODELVIEW: 779 return gl.GL_MODELVIEW_MATRIX
780
781 - def apply_to_gl(self):
782 """Set the OpenGL projection matrix.""" 783 gl.glMatrixMode(self.projection_type) 784 gl.glLoadMatrixf(self.parameters.matrix) # Need PyOpenGL >= 2.0
785
786 - def set_gl_modelview(self):
787 """Set the OpenGL modelview matrix.""" 788 gl.glMatrixMode(gl.GL_MODELVIEW) 789 gl.glLoadMatrixf(self.parameters.matrix) # Need PyOpenGL >= 2.0
790
791 - def set_gl_projection(self):
792 """Set the OpenGL projection matrix.""" 793 gl.glMatrixMode(gl.GL_PROJECTION) 794 gl.glLoadMatrixf(self.parameters.matrix) # Need PyOpenGL >= 2.0
795
796 - def push_and_set_gl_projection(self):
797 """Set the OpenGL projection matrix, pushing current projection matrix to stack.""" 798 gl.glMatrixMode(self.projection_type) # Set OpenGL matrix state to modify the projection matrix 799 gl.glPushMatrix() 800 gl.glLoadMatrixf(self.parameters.matrix) # Need PyOpenGL >= 2.0
801
802 - def translate(self,x,y,z):
803 """Compose a translation and set the OpenGL projection matrix.""" 804 gl.glMatrixMode(self.projection_type) # Set OpenGL matrix state to modify the projection matrix 805 gl.glLoadMatrixf(self.parameters.matrix) # Need PyOpenGL >= 2.0 806 gl.glTranslatef(x,y,z) 807 self.parameters.matrix = gl.glGetFloatv(self._get_matrix_type())
808
809 - def stateless_translate(self,x,y,z):
810 """Compose a translation without changing OpenGL state.""" 811 M = VisionEgg.ThreeDeeMath.TransformMatrix(self.parameters.matrix) 812 M.translate(x,y,z) 813 self.parameters.matrix = M.get_matrix()
814
815 - def rotate(self,angle_degrees,x,y,z):
816 """Compose a rotation and set the OpenGL projection matrix.""" 817 gl.glMatrixMode(self.projection_type) # Set OpenGL matrix state to modify the projection matrix 818 gl.glLoadMatrixf(self.parameters.matrix) # Need PyOpenGL >= 2.0 819 gl.glRotatef(angle_degrees,x,y,z) 820 self.parameters.matrix = gl.glGetFloatv(self._get_matrix_type())
821
822 - def stateless_rotate(self,angle_degrees,x,y,z):
823 """Compose a rotation without changing OpenGL state.""" 824 M = VisionEgg.ThreeDeeMath.TransformMatrix(self.parameters.matrix) 825 M.rotate(angle_degrees,x,y,z) 826 self.parameters.matrix = M.get_matrix()
827
828 - def scale(self,x,y,z):
829 """Compose a rotation and set the OpenGL projection matrix.""" 830 gl.glMatrixMode(self.projection_type) # Set OpenGL matrix state to modify the projection matrix 831 gl.glLoadMatrixf(self.parameters.matrix) # Need PyOpenGL >= 2.0 832 gl.glScalef(x,y,z) 833 self.parameters.matrix = gl.glGetFloatv(self._get_matrix_type())
834
835 - def stateless_scale(self,x,y,z):
836 """Compose a rotation without changing OpenGL state.""" 837 M = VisionEgg.ThreeDeeMath.TransformMatrix(self.parameters.matrix) 838 M.scale(x,y,z) 839 self.parameters.matrix = M.get_matrix()
840
841 - def get_matrix(self):
842 return self.parameters.matrix
843
844 - def look_at(self, eye, center, up ):
845 # Basically the same as gluLookAt 846 def normalize(vec): 847 numpy_vec = numpy.asarray(vec) 848 mag = math.sqrt(numpy.sum(numpy_vec**2)) 849 return numpy_vec / mag
850 def cross(vec1,vec2): 851 return ( vec1[1]*vec2[2] - vec1[2]*vec2[1], 852 vec1[2]*vec2[0] - vec1[0]*vec2[2], 853 vec1[0]*vec2[1] - vec1[1]*vec2[0] )
854 forward = numpy.array(( center[0] - eye[0], 855 center[1] - eye[1], 856 center[2] - eye[2]),'f') 857 forward = normalize(forward) 858 side = cross(forward,up) 859 side = normalize(side) 860 new_up = cross(side,forward) # recompute up 861 # XXX I might have to transpose this matrix 862 m = Numeric.array([[side[0], new_up[0], -forward[0], 0.0], 863 [side[1], new_up[1], -forward[1], 0.0], 864 [side[2], new_up[2], -forward[2], 0.0], 865 [ 0.0, 0.0, 0.0, 1.0]]) 866 # XXX This should get optimized -- don't do it in OpenGL 867 gl.glMatrixMode(self.projection_type) # Set OpenGL matrix state to modify the projection matrix 868 gl.glPushMatrix() # save current matrix 869 try: 870 gl.glLoadMatrixf(self.parameters.matrix) # Need PyOpenGL >= 2.0 871 gl.glMultMatrixf(m) 872 gl.glTranslatef(-eye[0],-eye[1],-eye[2]) 873 self.parameters.matrix = gl.glGetFloatv(self._get_matrix_type()) 874 finally: 875 gl.glPopMatrix() # save current matrix 876
877 - def eye_2_clip(self,eye_coords_vertex):
878 """Transform eye coordinates to clip coordinates""" 879 m = Numeric.array(self.parameters.matrix) 880 v = Numeric.array(eye_coords_vertex) 881 homog = VisionEgg.ThreeDeeMath.make_homogeneous_coord_rows(v) 882 r = numpy.dot(homog,m) 883 if len(homog.shape) > len(v.shape): 884 r = Numeric.reshape(r,(4,)) 885 return r
886 - def clip_2_norm_device(self,clip_coords_vertex):
887 """Transform clip coordinates to normalized device coordinates""" 888 v = numpy.array(clip_coords_vertex) 889 homog = VisionEgg.ThreeDeeMath.make_homogeneous_coord_rows(v) 890 err=numpy.seterr(all='ignore') 891 r = (homog/homog[:,3,numpy.newaxis])[:,:3] 892 numpy.seterr(**err) 893 if len(homog.shape) > len(v.shape): 894 r = Numeric.reshape(r,(3,)) 895 return r
896 - def eye_2_norm_device(self,eye_coords_vertex):
897 """Transform eye coordinates to normalized device coordinates""" 898 return self.clip_2_norm_device(self.eye_2_clip(eye_coords_vertex))
899
900 - def apply_to_vertex(self,vertex):
901 """Perform multiplication on vertex to get transformed result""" 902 M = VisionEgg.ThreeDeeMath.TransformMatrix(matrix=self.parameters.matrix) 903 r = M.transform_vertices([vertex]) 904 return r[0]
905
906 - def apply_to_vertices(self,vertices):
907 """Perform multiplication on vertex to get transformed result""" 908 M = VisionEgg.ThreeDeeMath.TransformMatrix(matrix=self.parameters.matrix) 909 r = M.transform_vertices(vertices) 910 return r
911
912 -class Projection(ProjectionBaseClass):
913 """for use of OpenGL PROJECTION_MATRIX 914 915 Parameters 916 ========== 917 matrix -- matrix specifying projection (Sequence4x4 of Real) 918 Default: [[1 0 0 0] 919 [0 1 0 0] 920 [0 0 1 0] 921 [0 0 0 1]] 922 """ 923
924 - def __init__(self,*args,**kw):
925 ProjectionBaseClass.__init__(self,*args,**kw) 926 self.projection_type = gl.GL_PROJECTION
927
928 -class ModelView(ProjectionBaseClass):
929 """for use of OpenGL MODELVIEW_MATRIX 930 931 Parameters 932 ========== 933 matrix -- matrix specifying projection (Sequence4x4 of Real) 934 Default: [[1 0 0 0] 935 [0 1 0 0] 936 [0 0 1 0] 937 [0 0 0 1]] 938 """ 939
940 - def __init__(self,*args,**kw):
941 ProjectionBaseClass.__init__(self,*args,**kw) 942 self.projection_type = gl.GL_MODELVIEW
943
944 -class OrthographicProjection(Projection):
945 """An orthographic projection. 946 947 Parameters 948 ========== 949 matrix -- matrix specifying projection (Sequence4x4 of Real) 950 Default: [[1 0 0 0] 951 [0 1 0 0] 952 [0 0 1 0] 953 [0 0 0 1]] 954 """ 955
956 - def __init__(self,left=0.0,right=640.0,bottom=0.0,top=480.0,z_clip_near=0.0,z_clip_far=1.0):
957 """Create an orthographic projection. 958 959 Defaults to map x eye coordinates in the range [0,640], y eye 960 coordinates [0,480] and clip coordinates [0,1] to [0,1]. 961 Therefore, if the viewport is 640 x 480, eye coordinates 962 correspond 1:1 with window (pixel) coordinates. Only points 963 between these clipping planes will be displayed. 964 """ 965 966 # using Numeric (from the OpenGL spec): 967 matrix = Numeric.array([[ 2./(right-left), 0., 0., -(right+left)/(right-left)], 968 [ 0., 2./(top-bottom), 0., -(top+bottom)/(top-bottom)], 969 [ 0., 0., -2./(z_clip_far-z_clip_near), -(z_clip_far+z_clip_near)/(z_clip_far-z_clip_near)], 970 [ 0., 0., 0., 1.0]]) 971 matrix = Numeric.transpose(matrix) # convert to OpenGL format 972 973 ## same as above, but use OpenGL 974 #gl.glMatrixMode(gl.GL_PROJECTION) 975 #gl.glPushMatrix() # save current matrix 976 #gl.glLoadIdentity() 977 #gl.glOrtho(left,right,bottom,top,z_clip_near,z_clip_far) 978 #matrix = gl.glGetFloatv(gl.GL_PROJECTION_MATRIX) 979 #gl.glPopMatrix() # restore original matrix 980 981 Projection.__init__(self,**{'matrix':matrix})
982
983 -class OrthographicProjectionNoZClip(Projection):
984 """An orthographic projection without Z clipping. 985 986 Parameters 987 ========== 988 matrix -- matrix specifying projection (Sequence4x4 of Real) 989 Default: [[1 0 0 0] 990 [0 1 0 0] 991 [0 0 1 0] 992 [0 0 0 1]] 993 """ 994
995 - def __init__(self,left=0.0,right=640.0,bottom=0.0,top=480.0):
996 """Create an orthographic projection without Z clipping. 997 998 Defaults to map x eye coordinates in the range [0,640] and y 999 eye coordinates [0,480] -> [0,1]. Therefore, if the viewport 1000 is 640 x 480, eye coordinates correspond 1:1 with window 1001 (pixel) coordinates. 1002 """ 1003 1004 # using Numeric (from the OpenGL spec): 1005 matrix = Numeric.array([[ 2./(right-left), 0, 0, -(right+left)/(right-left)], 1006 [ 0, 2./(top-bottom), 0, -(top+bottom)/(top-bottom)], 1007 [ 0, 0, -1, -1.], 1008 [ 0, 0, 0, 1]]) 1009 matrix = Numeric.transpose(matrix) # convert to OpenGL format 1010 1011 Projection.__init__(self,**{'matrix':matrix})
1012
1013 -class SimplePerspectiveProjection(Projection):
1014 """A simplified perspective projection. 1015 1016 Parameters 1017 ========== 1018 matrix -- matrix specifying projection (Sequence4x4 of Real) 1019 Default: [[1 0 0 0] 1020 [0 1 0 0] 1021 [0 0 1 0] 1022 [0 0 0 1]] 1023 """ 1024
1025 - def __init__(self,fov_x=45.0,z_clip_near = 0.1,z_clip_far=10000.0,aspect_ratio=4.0/3.0):
1026 matrix = self._compute_matrix(fov_x,z_clip_near,z_clip_far,aspect_ratio) 1027 Projection.__init__(self,**{'matrix':matrix})
1028
1029 - def _compute_matrix(self,fov_x=45.0,z_clip_near = 0.1,z_clip_far=10000.0,aspect_ratio=4.0/3.0):
1030 """Compute a 4x4 projection matrix that performs a perspective distortion.""" 1031 fov_y = fov_x / aspect_ratio 1032 # This is a translation of what gluPerspective does: 1033 #glu.gluPerspective(fov_y,aspect_ratio,z_clip_near,z_clip_far) 1034 radians = fov_y / 2.0 * math.pi / 180.0 1035 delta_z = z_clip_far - z_clip_near 1036 sine = math.sin(radians) 1037 if (delta_z == 0.0) or (sine == 0.0) or (aspect_ratio == 0.0): 1038 raise ValueError("Invalid parameters passed to SimpleProjection.__init__()") 1039 cotangent = math.cos(radians) / sine 1040 matrix = Numeric.zeros((4,4),'f') 1041 matrix[0][0] = cotangent/aspect_ratio 1042 matrix[1][1] = cotangent 1043 matrix[2][2] = -(z_clip_far + z_clip_near) / delta_z 1044 matrix[2][3] = -1.0 # XXX this 1045 matrix[3][2] = -2.0 * z_clip_near * z_clip_far / delta_z # XXX and this might cause the matrix to need to be transposed 1046 matrix[3][3] = 0.0 1047 return matrix
1048
1049 -class PerspectiveProjection(Projection):
1050 """A perspective projection. 1051 1052 Parameters 1053 ========== 1054 matrix -- matrix specifying projection (Sequence4x4 of Real) 1055 Default: [[1 0 0 0] 1056 [0 1 0 0] 1057 [0 0 1 0] 1058 [0 0 0 1]] 1059 """ 1060
1061 - def __init__(self,left,right,bottom,top,near,far):
1062 # XXX right now this is done in OpenGL, we should do it ourselves 1063 gl.glMatrixMode(gl.GL_PROJECTION) # Set OpenGL matrix state to modify the projection matrix 1064 gl.glPushMatrix() 1065 gl.glLoadIdentity() # Clear the projection matrix 1066 gl.glFrustum(left,right,bottom,top,near,far) # Let GL create a matrix and compose it 1067 matrix = gl.glGetFloatv(gl.GL_PROJECTION_MATRIX) 1068 gl.glPopMatrix() 1069 if matrix is None: 1070 # OpenGL wasn't started 1071 raise RuntimeError("OpenGL matrix operations can only take place once OpenGL context started.") 1072 if type(matrix) != Numeric.ArrayType: 1073 matrix = Numeric.array(matrix) # Convert to Numeric array 1074 Projection.__init__(self,**{'matrix':matrix})
1075 1076 #################################################################### 1077 # 1078 # Stimulus - Base class 1079 # 1080 #################################################################### 1081
1082 -class Stimulus(VisionEgg.ClassWithParameters):
1083 """Base class for a stimulus. 1084 1085 Any stimulus element should be a subclass of this Stimulus class. 1086 The draw() method contains the code executed before every buffer 1087 swap in order to render the stimulus to the frame buffer. It 1088 should execute as quickly as possible. The init_gl() method must 1089 be called before the first call to draw() so that any internal 1090 data, OpenGL display lists, and OpenGL:texture objects can be 1091 established. 1092 1093 To illustrate the concept of the Stimulus class, here is a 1094 description of several methods of drawing two spots. If your 1095 experiment displays two spots simultaneously, you could create two 1096 instances of (a single subclass of) Stimulus, varying parameters 1097 so each draws at a different location. Another possibility is to 1098 create one instance of a subclass that draws two spots. Another, 1099 somewhat obscure, possibility is to create a single instance and 1100 add it to two different viewports. (Something that will not work 1101 would be adding the same instance two times to the same viewport. 1102 It would also get drawn twice, although at exactly the same 1103 location.) 1104 1105 OpenGL is a 'state machine', meaning that it has internal 1106 parameters whose values vary and affect how it operates. Because 1107 of this inherent uncertainty, there are only limited assumptions 1108 about the state of OpenGL that an instance of Stimulus should 1109 expect when its draw() method is called. Because the Vision Egg 1110 loops through stimuli this also imposes some important behaviors: 1111 1112 First, the framebuffer will contain the results of any drawing 1113 operations performed since the last buffer swap by other instances 1114 of (subclasses of) Stimulus. Therefore, the order in which stimuli 1115 are present in the stimuli list of an instance of Viewport may be 1116 important. Additionally, if there are overlapping viewports, the 1117 order in which viewports are added to an instance of Screen is 1118 important. 1119 1120 Second, previously established OpenGL display lists and OpenGL 1121 texture objects will be available. The __init__() method should 1122 establish these things. 1123 1124 Third, there are several OpenGL state variables which are 1125 commonly set by subclasses of Stimulus, and which cannot be 1126 assumed to have any particular value at the time draw() is called. 1127 These state variables are: blending mode and function, texture 1128 state and environment, the matrix mode (modelview or projection), 1129 the modelview matrix, depth mode and settings. Therefore, if the 1130 draw() method depends on specific values for any of these states, 1131 it must specify its own values to OpenGL. 1132 1133 Finally, a well-behaved Stimulus subclass resets any OpenGL state 1134 values other than those listed above to their initial state before 1135 draw() and init_gl() were called. In other words, before your 1136 stimulus changes the state of an OpenGL variable, use 1137 glGetBoolean, glGetInteger, glGetFloat, or a similar function to 1138 query its value and restore it later. For example, upon calling 1139 the draw() method, the projection matrix will be that which was 1140 set by the viewport. If the draw() method alters the projection 1141 matrix, it must be restored. The glPushMatrix() and glPopMatrix() 1142 commands provide an easy way to do this. 1143 1144 The default projection of Viewport maps eye coordinates in a 1:1 1145 fashion to window coordinates (in other words, it sets eye 1146 coordinates to use pixel units from the lower left corner of the 1147 viewport). Therefore the default parameters for a stimulus should 1148 specify pixel coordinates if possible (such as for a 2D 1149 stimulus). Assuming a window size of 640 by 480 for the default 1150 parameters is a pretty safe way to do things. 1151 1152 Also, be sure to check for any assumptions made about the system 1153 in the __init__ method. For example, if your stimulus needs alpha 1154 in the framebuffer, check the value of 1155 glGetIntegerv(GL_ALPHA_BITS) and raise an exception if it is not 1156 available. 1157 """ 1158
1159 - def __init__(self,**kw):
1160 """Instantiate and get ready to draw. 1161 1162 Set parameter values and create anything needed to draw the 1163 stimulus including OpenGL state variables such display lists 1164 and texture objects. 1165 1166 """ 1167 VisionEgg.ClassWithParameters.__init__(self,**kw)
1168
1169 - def draw(self):
1170 """Draw the stimulus. (Called by Viewport instance.) 1171 1172 This method is called every frame. This method actually 1173 performs the OpenGL calls to draw the stimulus. 1174 1175 """ 1176 pass
1177 1178 #################################################################### 1179 # 1180 # Viewport 1181 # 1182 #################################################################### 1183
1184 -class Viewport(VisionEgg.ClassWithParameters):
1185 """Connects stimuli to a screen. 1186 1187 A viewport defines a (possibly clipped region) of the screen on 1188 which stimuli are drawn. 1189 1190 A screen may have multiple viewports. The viewports may be 1191 overlapping. 1192 1193 A viewport may have multiple stimuli. 1194 1195 A single stimulus may be drawn simultaneously by several 1196 viewports, although this is typically useful only for 3D stimuli 1197 to represent different views of the same object. 1198 1199 The coordinates of the stimulus are converted to screen 1200 coordinates via several steps, the most important of which is the 1201 projection, which is defined by an instance of the Projection 1202 class. 1203 1204 By default, a viewport has a projection which maps eye coordinates 1205 to viewport coordinates in 1:1 manner. In other words, eye 1206 coordinates specify pixel location in the viewport. 1207 1208 For cases where pixel units are not natural to describe 1209 coordinates of a stimulus, the application should specify the a 1210 projection other than the default. This is usually the case for 1211 3D stimuli. 1212 1213 For details of the projection and clipping process, see the 1214 section 'Coordinate Transformations' in the book/online document 1215 'The OpenGL Graphics System: A Specification' 1216 1217 Parameters 1218 ========== 1219 anchor -- How position parameter is interpreted (String) 1220 Default: lowerleft 1221 camera_matrix -- extrinsic camera parameter matrix (position and orientation) (Instance of <class 'VisionEgg.Core.ModelView'>) 1222 Default: (determined at runtime) 1223 depth_range -- depth range (in object units) for rendering (Sequence2 of Real) 1224 Default: (0, 1) 1225 position -- Position (in pixel units) within the screen (Sequence2 of Real) 1226 Default: (0, 0) 1227 projection -- intrinsic camera parameter matrix (field of view, focal length, aspect ratio) (Instance of <class 'VisionEgg.Core.Projection'>) 1228 Default: (determined at runtime) 1229 screen -- The screen in which this viewport is drawn (Instance of <class 'VisionEgg.Core.Screen'>) 1230 Default: (determined at runtime) 1231 size -- Size (in pixel units) (Sequence2 of Real) 1232 Default: (determined at runtime) 1233 stimuli -- sequence of stimuli to draw in screen (Sequence of Instance of <class 'VisionEgg.Core.Stimulus'>) 1234 Default: (determined at runtime) 1235 """ 1236 1237 parameters_and_defaults = VisionEgg.ParameterDefinition({ 1238 'screen':(None, 1239 ve_types.Instance(Screen), 1240 'The screen in which this viewport is drawn'), 1241 'position':((0,0), 1242 ve_types.Sequence2(ve_types.Real), 1243 'Position (in pixel units) within the screen'), 1244 'anchor':('lowerleft', 1245 ve_types.String, 1246 'How position parameter is interpreted'), 1247 'depth_range':((0,1), 1248 ve_types.Sequence2(ve_types.Real), 1249 'depth range (in object units) for rendering'), 1250 'size':(None, # will use screen.size if not specified 1251 ve_types.Sequence2(ve_types.Real), 1252 'Size (in pixel units)'), 1253 'projection':(None, 1254 ve_types.Instance(Projection), 1255 'intrinsic camera parameter matrix (field of view, focal length, aspect ratio)'), 1256 'camera_matrix':(None, 1257 ve_types.Instance(ModelView), 1258 'extrinsic camera parameter matrix (position and orientation)'), 1259 'stimuli':(None, 1260 ve_types.Sequence(ve_types.Instance(Stimulus)), 1261 'sequence of stimuli to draw in screen'), 1262 'lowerleft':(None, # DEPRECATED -- don't use 1263 ve_types.Sequence2(ve_types.Real), 1264 'position (in pixel units) of lower-left viewport corner', 1265 VisionEgg.ParameterDefinition.DEPRECATED), 1266 }) 1267 1268 __slots__ = ( 1269 '_is_drawing', 1270 ) 1271
1272 - def __init__(self,**kw):
1273 """Create a new instance. 1274 1275 Required arguments: 1276 1277 screen 1278 1279 Optional arguments (specify parameter value other than default): 1280 1281 position -- defaults to (0,0), position relative to screen by anchor (see below) 1282 anchor -- defaults to 'lowerleft' 1283 size -- defaults to screen.size 1284 projection -- defaults to self.make_new_pixel_coord_projection() 1285 stimuli -- defaults to empty list 1286 """ 1287 VisionEgg.ClassWithParameters.__init__(self,**kw) 1288 1289 if self.parameters.screen is None: 1290 raise ValueError("Must specify screen when creating an instance of Viewport.") 1291 1292 p = self.parameters # shorthand 1293 if p.size is None: 1294 p.size = p.screen.constant_parameters.size 1295 if p.projection is None: 1296 # Default projection maps eye coordinates 1:1 on window (pixel) coordinates 1297 p.projection = self.make_new_pixel_coord_projection() 1298 if p.camera_matrix is None: 1299 p.camera_matrix = ModelView() 1300 if p.stimuli is None: 1301 p.stimuli = [] 1302 self._is_drawing = False
1303
1305 """Create instance of Projection mapping eye coordinates 1:1 with pixel coordinates.""" 1306 return OrthographicProjectionNoZClip(left=0,right=self.parameters.size[0], 1307 bottom=0,top=self.parameters.size[1])
1308
1309 - def make_current(self):
1310 p = self.parameters # shorthand 1311 p.screen.make_current() 1312 1313 if p.lowerleft != None: 1314 if not hasattr(Viewport,"_gave_lowerleft_warning"): 1315 logger = logging.getLogger('VisionEgg.Core') 1316 logger.warning("lowerleft parameter of Viewport class " 1317 "will stop being supported. Use " 1318 "'position' instead with anchor set to " 1319 "'lowerleft'.") 1320 Viewport._gave_lowerleft_warning = True 1321 p.anchor = 'lowerleft' 1322 p.position = p.lowerleft[0], p.lowerleft[1] # copy values (don't copy ref to tuple) 1323 1324 lowerleft = VisionEgg._get_lowerleft(p.position,p.anchor,p.size) 1325 1326 gl.glViewport(lowerleft[0], 1327 lowerleft[1], 1328 p.size[0], 1329 p.size[1]) 1330 gl.glDepthRange(p.depth_range[0],p.depth_range[1]) 1331 1332 p.projection.apply_to_gl() 1333 p.camera_matrix.apply_to_gl()
1334
1335 - def draw(self):
1336 """Set the viewport and draw stimuli.""" 1337 self.make_current() 1338 self._is_drawing = True 1339 for stimulus in self.parameters.stimuli: 1340 stimulus.draw() 1341 self._is_drawing = False
1342
1343 - def norm_device_2_window(self,norm_device_vertex):
1344 """Transform normalized device coordinates to window coordinates""" 1345 v = Numeric.asarray(norm_device_vertex) 1346 homog = VisionEgg.ThreeDeeMath.make_homogeneous_coord_rows(v) 1347 xd = homog[:,0,Numeric.NewAxis] 1348 yd = homog[:,1,Numeric.NewAxis] 1349 zd = homog[:,2,Numeric.NewAxis] 1350 1351 p = self.parameters # shorthand 1352 lowerleft = VisionEgg._get_lowerleft(p.position,p.anchor,p.size) 1353 x,y = lowerleft 1354 w,h = p.size 1355 n,f = p.depth_range 1356 1357 # clamp n and f 1358 n = min(1.0,max(0.0,n)) 1359 f = min(1.0,max(0.0,f)) 1360 1361 ox = x + w/2.0 1362 oy = y + h/2.0 1363 px = w 1364 py = h 1365 xw = (px/2.0)*xd + ox 1366 yw = (py/2.0)*yd + oy 1367 zw = ((f-n)/2.0)*zd + (n+f)/2.0 1368 # XXX I think zw (or zd) is clamped in OpenGL, but I can't 1369 # find it in any spec! 1370 #zw = Numeric.clip(zw,0.0,1.0) # clamp 1371 r = Numeric.concatenate((xw,yw,zw),axis=1) 1372 if len(homog.shape) > len(v.shape): 1373 r = Numeric.reshape(r,(3,)) 1374 return r
1375 - def clip_2_window(self,eye_coords_vertex):
1376 """Transform clip coordinates to window coordinates""" 1377 my_proj = self.parameters.projection 1378 return self.norm_device_2_window( my_proj.clip_2_norm_device( eye_coords_vertex ) )
1379 - def eye_2_window(self,eye_coords_vertex):
1380 """Transform eye coordinates to window coordinates""" 1381 my_proj = self.parameters.projection 1382 return self.norm_device_2_window( my_proj.eye_2_norm_device( eye_coords_vertex ) )
1383 1384 #################################################################### 1385 # 1386 # FixationSpot 1387 # 1388 #################################################################### 1389
1390 -class FixationSpot(Stimulus):
1391 """A rectangle stimulus, typically used as a fixation spot. 1392 1393 Parameters 1394 ========== 1395 anchor -- how position parameter is used (String) 1396 Default: center 1397 color -- color (AnyOf(Sequence3 of Real or Sequence4 of Real)) 1398 Default: (1.0, 1.0, 1.0) 1399 on -- draw? (Boolean) 1400 Default: True 1401 position -- position in eye coordinates (AnyOf(Sequence2 of Real or Sequence3 of Real or Sequence4 of Real)) 1402 Default: (320.0, 240.0) 1403 size -- size in eye coordinates (Sequence2 of Real) 1404 Default: (4.0, 4.0) 1405 """ 1406 1407 parameters_and_defaults = VisionEgg.ParameterDefinition({ 1408 'on':(True, 1409 ve_types.Boolean, 1410 'draw?'), 1411 'color':((1.0,1.0,1.0), 1412 ve_types.AnyOf(ve_types.Sequence3(ve_types.Real), 1413 ve_types.Sequence4(ve_types.Real)), 1414 'color'), 1415 'position' : ( ( 320.0, 240.0 ), # in eye coordinates 1416 ve_types.AnyOf(ve_types.Sequence2(ve_types.Real), 1417 ve_types.Sequence3(ve_types.Real), 1418 ve_types.Sequence4(ve_types.Real)), 1419 'position in eye coordinates'), 1420 'anchor' : ('center', 1421 ve_types.String, 1422 'how position parameter is used'), 1423 'size':((4.0,4.0), # horiz and vertical size 1424 ve_types.Sequence2(ve_types.Real), 1425 'size in eye coordinates'), 1426 'center' : (None, # DEPRECATED -- don't use 1427 ve_types.Sequence2(ve_types.Real), 1428 'position in eye coordinates', 1429 VisionEgg.ParameterDefinition.DEPRECATED), 1430 }) 1431
1432 - def __init__(self,**kw):
1433 Stimulus.__init__(self,**kw)
1434
1435 - def draw(self):
1436 p = self.parameters # shorthand 1437 if p.center is not None: 1438 if not hasattr(VisionEgg.config,"_GAVE_CENTER_DEPRECATION"): 1439 logger = logging.getLogger('VisionEgg.Core') 1440 logger.warning("Specifying FixationSpot by deprecated " 1441 "'center' parameter deprecated. Use " 1442 "'position' parameter instead. (Allows " 1443 "use of 'anchor' parameter to set to " 1444 "other values.)") 1445 VisionEgg.config._GAVE_CENTER_DEPRECATION = 1 1446 p.anchor = 'center' 1447 p.position = p.center[0], p.center[1] # copy values (don't copy ref to tuple) 1448 if p.on: 1449 # calculate center 1450 center = VisionEgg._get_center(p.position,p.anchor,p.size) 1451 gl.glDisable(gl.GL_DEPTH_TEST) 1452 gl.glDisable(gl.GL_TEXTURE_2D) 1453 gl.glDisable(gl.GL_BLEND) 1454 1455 if len(p.color)==3: 1456 gl.glColor3f(*p.color) 1457 elif len(p.color)==4: 1458 gl.glColor4f(*p.color) 1459 1460 # This could go in a display list to speed it up, but then 1461 # size wouldn't be dynamically adjustable this way. Could 1462 # still use one of the matrices to make it change size. 1463 x_size = self.parameters.size[0]/2.0 1464 y_size = self.parameters.size[1]/2.0 1465 x,y = center[0],center[1] 1466 x1 = x-x_size; x2 = x+x_size 1467 y1 = y-y_size; y2 = y+y_size 1468 gl.glBegin(gl.GL_QUADS) 1469 gl.glVertex2f(x1,y1) 1470 gl.glVertex2f(x2,y1) 1471 gl.glVertex2f(x2,y2) 1472 gl.glVertex2f(x1,y2) 1473 gl.glEnd() # GL_QUADS
1474 1475 #################################################################### 1476 # 1477 # Frame timing information 1478 # 1479 #################################################################### 1480
1481 -class FrameTimer:
1482 """Time inter frame intervals and compute frames per second."""
1483 - def __init__(self, bin_start_msec=2, bin_stop_msec=28, bin_width_msec=2, running_average_num_frames=0,save_all_frametimes=False):
1484 """Create instance of FrameTimer.""" 1485 self.bins = Numeric.arange( bin_start_msec, bin_stop_msec, bin_width_msec ) 1486 self.bin_width_msec = float(bin_width_msec) 1487 self.timing_histogram = Numeric.zeros( self.bins.shape, Numeric.Float ) # make float to avoid (early) overflow errors 1488 self._true_time_last_frame = None # no frames yet 1489 self.longest_frame_draw_time_sec = None 1490 self.first_tick_sec = None 1491 self.total_frames = 0 1492 self.running_average_num_frames = running_average_num_frames 1493 if self.running_average_num_frames: 1494 self.last_n_frame_times_sec = [None]*self.running_average_num_frames 1495 self.save_all_frametimes = save_all_frametimes 1496 if self.save_all_frametimes: 1497 self.all_frametimes = []
1498
1499 - def tick(self):
1500 """Declare a frame has just been drawn.""" 1501 true_time_now = VisionEgg.true_time_func() 1502 if self._true_time_last_frame != None: 1503 this_frame_draw_time_sec = true_time_now - self._true_time_last_frame 1504 index = int(math.ceil(this_frame_draw_time_sec*1000.0/self.bin_width_msec))-1 1505 if index > (len(self.timing_histogram)-1): 1506 index = -1 1507 self.timing_histogram[index] += 1 1508 self.longest_frame_draw_time_sec = max(self.longest_frame_draw_time_sec,this_frame_draw_time_sec) 1509 if self.running_average_num_frames: 1510 self.last_n_frame_times_sec.append(true_time_now) 1511 self.last_n_frame_times_sec.pop(0) 1512 else: 1513 self.first_tick_sec = true_time_now 1514 self._true_time_last_frame = true_time_now # set for next frame 1515 1516 if self.save_all_frametimes: 1517 self.all_frametimes.append( true_time_now )
1518
1519 - def get_all_frametimes(self):
1520 if self.save_all_frametimes: 1521 return self.all_frametimes 1522 else: 1523 raise ValueError("must set save_all_frametimes")
1524
1526 return self.longest_frame_draw_time_sec
1527
1529 if self.running_average_num_frames: 1530 frame_times = [] 1531 for frame_time in self.last_n_frame_times_sec: 1532 if frame_time is not None: 1533 frame_times.append( frame_time ) 1534 if len(frame_times) >= 2: 1535 return (frame_times[-1] - frame_times[0]) / len(frame_times) 1536 else: 1537 raise RuntimeError("running_average_num_frames not set when creating FrameTimer instance")
1538
1539 - def get_average_ifi_sec(self):
1540 if self._true_time_last_frame is None: 1541 raise RuntimeError("No frames were drawn, can't calculate average IFI") 1542 return (self._true_time_last_frame - self.first_tick_sec) / sum( self.timing_histogram )
1543
1544 - def print_histogram(self):
1545 logger = logging.getLogger('VisionEgg.Core') 1546 logger.warning("print_histogram() method of FrameTimer is " 1547 "deprecated will stop being supported. Use " 1548 "log_histogram() instead.") 1549 self.log_histogram()
1550
1551 - def log_histogram(self):
1552 """Send histogram to logger.""" 1553 buffer = StringIO.StringIO() 1554 1555 n_frames = sum( self.timing_histogram )+1 1556 if n_frames < 2: 1557 print >> buffer, '%d frames were drawn.'%n_frames 1558 return 1559 average_ifi_sec = self.get_average_ifi_sec() 1560 print >> buffer, '%d frames were drawn.'%int(n_frames) 1561 print >> buffer, 'Mean IFI was %.2f msec (%.2f fps), longest IFI was %.2f msec.'%( 1562 average_ifi_sec*1000.0,1.0/average_ifi_sec,self.longest_frame_draw_time_sec*1000.0) 1563 1564 h = hist = self.timing_histogram # shorthand 1565 maxhist = float(max(h)) 1566 if maxhist == 0: 1567 print >> buffer, "No frames were drawn." 1568 return 1569 lines = min(10,int(math.ceil(maxhist))) 1570 hist = hist/maxhist*float(lines) # normalize to number of lines 1571 print >> buffer, "histogram:" 1572 for line in range(lines): 1573 val = float(lines)-1.0-float(line) 1574 timing_string = "%6d "%(round(maxhist*val/lines),) 1575 q = Numeric.greater(hist,val) 1576 for qi in q: 1577 s = ' ' 1578 if qi: 1579 s = '*' 1580 timing_string += "%4s "%(s,) 1581 print >> buffer, timing_string 1582 timing_string = " Time: " 1583 timing_string += "%4d "%(0,) 1584 for bin in self.bins[:-1]: 1585 timing_string += "%4d "%(bin,) 1586 timing_string += "+(msec)\n" 1587 timing_string += "Total: " 1588 for hi in h: 1589 if hi <= 999: 1590 num_str = str(int(hi)).center(5) 1591 else: 1592 num_str = " +++ " 1593 timing_string += num_str 1594 print >> buffer, timing_string 1595 1596 buffer.seek(0) 1597 logger = logging.getLogger('VisionEgg.Core') 1598 logger.info(buffer.read())
1599 1600 #################################################################### 1601 # 1602 # Error handling and assumption checking 1603 # 1604 #################################################################### 1605 1606 import VisionEgg.Deprecated 1607 Message = VisionEgg.Deprecated.Message 1608 1609 message = VisionEgg.Deprecated.Message() # create instance of Message class for everything to use 1610 1611 gl_assumptions = [] 1612
1613 -def add_gl_assumption(gl_variable,required_value,failure_callback):
1614 """Save assumptions for later checking once OpenGL context created.""" 1615 if type(failure_callback) != types.FunctionType: 1616 raise ValueError("failure_callback must be a function!") 1617 gl_assumptions.append((gl_variable,required_value,failure_callback))
1618
1619 -def init_gl_extension(prefix,name):
1620 global gl # interpreter knows when we're up to something funny with GLTrace 1621 logger = logging.getLogger('VisionEgg.Core') 1622 1623 if gl is VisionEgg.GLTrace: 1624 watched = True 1625 gl = VisionEgg.GLTrace.gl # manipulate original module for now 1626 else: 1627 watched = False 1628 1629 module_name = "OpenGL.GL.%(prefix)s.%(name)s"%locals() 1630 try: 1631 exec "import "+module_name 1632 except ImportError: 1633 logger.warning("Could not import %s -- some features will be " 1634 "missing."%(module_name,)) 1635 return False 1636 module = eval(module_name) 1637 init_function_name = "glInit"+name.title().replace('_','')+prefix 1638 init_function = getattr(module,init_function_name) 1639 if not init_function(): 1640 logger.warning("Could not initialize %s -- some features will " 1641 "be missing."%(module_name,)) 1642 return False 1643 for attr_name in dir(module): 1644 # put attributes from module into "gl" module dictionary 1645 # (Namespace overlap as you'd get OpenGL apps written in C) 1646 attr = getattr(module,attr_name) 1647 # reject unwanted attributes 1648 if attr_name.startswith('__'): 1649 continue 1650 elif attr_name == init_function_name: 1651 continue 1652 elif attr_name == 'gl': 1653 continue 1654 elif type(attr) == type(VisionEgg): # module type 1655 continue 1656 1657 gl_attr_name = attr_name 1658 setattr(gl,gl_attr_name,attr) 1659 1660 if watched: 1661 VisionEgg.GLTrace.gl_trace_attach() # (re)scan namespace 1662 gl = VisionEgg.GLTrace # reinstall GLTrace 1663 return True # success!
1664
1665 -def post_gl_init():
1666 """Called by Screen instance. Requires OpenGL context to be created.""" 1667 global gl_vendor, gl_renderer, gl_version # set above 1668 logger = logging.getLogger('VisionEgg.Core') 1669 1670 if gl_version < '1.3': 1671 if not init_gl_extension('ARB','multitexture'): 1672 logger.warning("multitexturing not available. Some features " 1673 "will not be available") 1674 else: 1675 if not hasattr(gl,'glActiveTexture'): 1676 logger.debug("PyOpenGL bug: OpenGL multitexturing not available " 1677 "even though OpenGL is 1.3 or greater. " 1678 "Attempting ctypes-based workaround.") 1679 VisionEgg.PlatformDependent.attempt_to_load_multitexturing() 1680 if hasattr(gl,'glActiveTexture'): # the above worked or PyOpenGL fixed 1681 # OpenGL 1.3 has this extension built-in, 1682 # but doing this allows use of ARB names. 1683 gl.glActiveTextureARB = gl.glActiveTexture 1684 gl.glMultiTexCoord2fARB = gl.glMultiTexCoord2f 1685 gl.GL_TEXTURE0_ARB = gl.GL_TEXTURE0 1686 gl.GL_TEXTURE1_ARB = gl.GL_TEXTURE1 1687 1688 if gl_version < '1.2': 1689 if init_gl_extension('EXT','bgra'): 1690 # make sure gl.GL_BRGA is defined 1691 gl.GL_BGRA = gl.GL_BGRA_EXT 1692 1693 for gl_variable,required_value,failure_callback in gl_assumptions: 1694 # Code required for each variable to be checked 1695 if gl_variable == "__SPECIAL__": 1696 if required_value == "linux_nvidia_or_new_ATI": 1697 ok = 0 1698 # Test for nVidia 1699 if "nvidia" == gl_vendor.split()[0].lower(): 1700 ok = 1 # yes it is 1701 if gl_renderer.startswith('Mesa DRI Radeon'): 1702 date = gl_renderer.split()[3] 1703 if date > "20021216": # not sure about exact date 1704 ok=1 1705 if not ok: 1706 failure_callback() 1707 else: 1708 raise RuntimeError, "Unknown gl_assumption: %s == %s"%(gl_variable,required_value) 1709 1710 elif gl_variable.upper() == "GL_VERSION": 1711 value_str = gl_version.split()[0] 1712 value_ints = map(int,value_str.split('.')) 1713 value = float( str(value_ints[0]) + "." + ''.join(map(str,value_ints[1:]))) 1714 if value < required_value: 1715 failure_callback() 1716 else: 1717 raise RuntimeError, "Unknown gl_assumption" 1718 1719 # Do we have gl.GL_CLAMP_TO_EDGE ? 1720 try: 1721 gl.GL_CLAMP_TO_EDGE 1722 except AttributeError: 1723 if gl_version >= '1.2': 1724 # If OpenGL version >= 1.2, this should be defined 1725 # It seems to be a PyOpenGL bug that it's not. 1726 logger.debug("GL_CLAMP_TO_EDGE is not defined. " 1727 "Because you have OpenGL version 1.2 or " 1728 "greater, this is probably a bug in " 1729 "PyOpenGL. Assigning GL_CLAMP_TO_EDGE to " 1730 "the value that is usually used.") 1731 gl.GL_CLAMP_TO_EDGE = 0x812F 1732 else: 1733 try: 1734 init_gl_extension('SGIS','texture_edge_clamp') 1735 gl.GL_CLAMP_TO_EDGE = gl.GL_CLAMP_TO_EDGE_SGIS 1736 except: 1737 1738 logger.warning("GL_CLAMP_TO_EDGE is not " 1739 "available. OpenGL version is " 1740 "less than 1.2, and the " 1741 "texture_edge_clamp_SGIS extension " 1742 "failed to load. It may be impossible to " 1743 "get exact 1:1 reproduction of " 1744 "textures. Using GL_CLAMP instead of " 1745 "GL_CLAMP_TO_EDGE.") 1746 gl.GL_CLAMP_TO_EDGE = gl.GL_CLAMP
1747 1748 ######################################################################### 1749 # 1750 # Moved to FlowControl.py -- here only for backwards compatibility 1751 # 1752 ######################################################################### 1753 1754 import VisionEgg.FlowControl 1755 Presentation = VisionEgg.FlowControl.Presentation 1756 Controller = VisionEgg.FlowControl.Controller 1757 ConstantController = VisionEgg.FlowControl.ConstantController 1758 EvalStringController = VisionEgg.FlowControl.EvalStringController 1759 ExecStringController = VisionEgg.FlowControl.ExecStringController 1760 FunctionController = VisionEgg.FlowControl.FunctionController 1761 EncapsulatedController = VisionEgg.FlowControl.EncapsulatedController 1762