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

Source Code for Module VisionEgg.SphereMap

   1  # The Vision Egg: SphereMap 
   2  # 
   3  # Copyright (C) 2001-2004 Andrew Straw. 
   4  # Copyright (C) 2005-2008 California Institute of Technology 
   5  # 
   6  # Author: Andrew Straw <astraw@users.sourceforge.net> 
   7  # URL: <http://www.visionegg.org/> 
   8  # 
   9  # Distributed under the terms of the GNU Lesser General Public License 
  10  # (LGPL). See LICENSE.TXT that came with this file. 
  11   
  12  """ 
  13  Stimuli on spheres, including texture maps. 
  14   
  15  """ 
  16   
  17  import math, types 
  18   
  19  import logging 
  20   
  21  import VisionEgg.Core 
  22  import VisionEgg.Textures 
  23  import VisionEgg.Text 
  24  import VisionEgg.Gratings 
  25  import VisionEgg.ThreeDeeMath 
  26  import VisionEgg.ParameterTypes as ve_types 
  27   
  28  import numpy 
  29  import numpy.oldnumeric as Numeric 
  30  import Image 
  31   
  32  import VisionEgg.GL as gl # get all OpenGL stuff in one namespace 
  33   
  34  __version__ = VisionEgg.release_name 
  35   
36 -class AzElGrid(VisionEgg.Core.Stimulus):
37 """Spherical grid of iso-azimuth and iso-elevation lines. 38 39 Parameters 40 ========== 41 anti_aliasing -- (Boolean) 42 Default: True 43 center_azimuth -- (Real) 44 Default: 0.0 45 center_elevation -- (Real) 46 Default: 0.0 47 major_line_color -- (AnyOf(Sequence3 of Real or Sequence4 of Real)) 48 Default: (0.0, 0.0, 0.0) 49 major_line_width -- (Real) 50 Default: 2.0 51 minor_line_color -- (AnyOf(Sequence3 of Real or Sequence4 of Real)) 52 Default: (0.0, 0.0, 1.0) 53 minor_line_width -- (Real) 54 Default: 1.0 55 my_viewport -- (Instance of <class 'VisionEgg.Core.Viewport'>) 56 Default: (determined at runtime) 57 on -- (Boolean) 58 Default: True 59 text_offset -- (Sequence2 of Real) 60 Default: (3, -2) 61 62 Constant Parameters 63 =================== 64 az_major_spacing -- (Real) 65 Default: 30.0 66 az_minor_spacing -- (Real) 67 Default: 10.0 68 el_major_spacing -- (Real) 69 Default: 30.0 70 el_minor_spacing -- (Real) 71 Default: 10.0 72 font_size -- (UnsignedInteger) 73 Default: 24 74 num_samples_per_circle -- (UnsignedInteger) 75 Default: 100 76 radius -- (Real) 77 Default: 1.0 78 text_anchor -- (String) 79 Default: lowerleft 80 text_color -- (AnyOf(Sequence3 of Real or Sequence4 of Real)) 81 Default: (0.0, 0.0, 0.0) 82 use_text -- (Boolean) 83 Default: True 84 """ 85 86 parameters_and_defaults = { 87 'on':(True, 88 ve_types.Boolean), 89 'center_azimuth':(0.0, # 0=right, 90=right 90 ve_types.Real), 91 'center_elevation':(0.0, # 0=right, 90=up 92 ve_types.Real), 93 'minor_line_width':(1.0, 94 ve_types.Real), 95 'major_line_width':(2.0, 96 ve_types.Real), 97 'minor_line_color':((0.0,0.0,1.0), 98 ve_types.AnyOf(ve_types.Sequence3(ve_types.Real), 99 ve_types.Sequence4(ve_types.Real))), 100 'major_line_color':((0.0,0.0,0.0), 101 ve_types.AnyOf(ve_types.Sequence3(ve_types.Real), 102 ve_types.Sequence4(ve_types.Real))), 103 'my_viewport':(None, # viewport I'm in 104 ve_types.Instance(VisionEgg.Core.Viewport)), 105 'text_offset':((3,-2), # offset (x,y) to nudge text labels 106 ve_types.Sequence2(ve_types.Real)), 107 'anti_aliasing' : ( True, 108 ve_types.Boolean ), 109 } 110 111 constant_parameters_and_defaults = { 112 'use_text':(True, 113 ve_types.Boolean), 114 'radius':(1.0, 115 ve_types.Real), 116 'az_minor_spacing':(10.0, 117 ve_types.Real), 118 'az_major_spacing':(30.0, 119 ve_types.Real), 120 'el_minor_spacing':(10.0, 121 ve_types.Real), 122 'el_major_spacing':(30.0, 123 ve_types.Real), 124 'num_samples_per_circle':(100, 125 ve_types.UnsignedInteger), 126 'font_size':(24, 127 ve_types.UnsignedInteger), 128 'text_color':((0.0,0.0,0.0), 129 ve_types.AnyOf(ve_types.Sequence3(ve_types.Real), 130 ve_types.Sequence4(ve_types.Real))), 131 'text_anchor':('lowerleft', 132 ve_types.String), 133 } 134 135 __slots__ = ( 136 'cached_minor_lines_display_list', 137 'cached_major_lines_display_list', 138 'text_viewport', 139 'text_viewport_orig', 140 '_gave_alpha_warning', 141 'labels', 142 'labels_xyz', 143 ) 144
145 - def __init__(self,**kw):
146 VisionEgg.Core.Stimulus.__init__(self,**kw) 147 self.cached_minor_lines_display_list = gl.glGenLists(1) # Allocate a new display list 148 self.cached_major_lines_display_list = gl.glGenLists(1) # Allocate a new display list 149 self.__rebuild_display_lists() 150 self.text_viewport = None # not set yet 151 self._gave_alpha_warning = False
152
153 - def __rebuild_display_lists(self):
154 def get_xyz(theta,phi,radius): 155 # theta normally between 0 and pi (north pole to south pole) 156 # phi between -pi and pi 157 y = radius * math.cos( theta ) 158 w = radius * math.sin( theta ) 159 x = w * math.cos( phi ) 160 z = w * math.sin( phi ) 161 return x,y,z
162 def draw_half_great_circle(az): 163 for i in range(cp.num_samples_per_circle/2): 164 # let theta exceed 1 pi to draw 2nd half of circle 165 theta_start = i/float(cp.num_samples_per_circle)*2*math.pi 166 theta_stop = (i+1)/float(cp.num_samples_per_circle)*2*math.pi 167 phi_start = phi_stop = (az-90.0)/180.0*math.pi 168 x_start,y_start,z_start = get_xyz(theta_start,phi_start,cp.radius) 169 x_stop,y_stop,z_stop = get_xyz(theta_stop,phi_stop,cp.radius) 170 gl.glVertex3f(x_start, y_start, z_start) 171 gl.glVertex3f(x_stop, y_stop, z_stop)
172 def draw_iso_elevation_circle(el): 173 # el from -90 = pi to el 90 = 0 174 theta_start = theta_stop = -(el-90) / 180.0 * math.pi 175 for i in range(cp.num_samples_per_circle): 176 phi_start = i/float(cp.num_samples_per_circle)*2*math.pi 177 phi_stop = (i+1)/float(cp.num_samples_per_circle)*2*math.pi 178 x_start,y_start,z_start = get_xyz(theta_start,phi_start,cp.radius) 179 x_stop,y_stop,z_stop = get_xyz(theta_stop,phi_stop,cp.radius) 180 gl.glVertex3f(x_start, y_start, z_start) 181 gl.glVertex3f(x_stop, y_stop, z_stop) 182 183 cp = self.constant_parameters 184 # Weird range construction to be sure to include zero. 185 azs_major = numpy.concatenate(( 186 numpy.arange(0.0,180.0,cp.az_major_spacing), 187 -numpy.arange(0.0,180.0,cp.az_major_spacing)[1:])) 188 azs_minor = numpy.concatenate(( 189 numpy.arange(0.0,180.0,cp.az_minor_spacing), 190 -numpy.arange(0.0,180.0,cp.az_minor_spacing)[1:])) 191 els_major = numpy.concatenate(( 192 numpy.arange(0.0,90.0,cp.el_major_spacing), 193 -numpy.arange(0.0,90.0,cp.el_major_spacing)[1:])) 194 els_minor = numpy.concatenate(( 195 numpy.arange(0.0,90.0,cp.el_minor_spacing), 196 -numpy.arange(0.0,90.0,cp.el_minor_spacing)[1:])) 197 198 gl.glNewList(self.cached_minor_lines_display_list,gl.GL_COMPILE) 199 gl.glBegin(gl.GL_LINES) 200 # az minor 201 for az in azs_minor: 202 if az in azs_major: 203 continue # draw only once as major 204 draw_half_great_circle(az) 205 for el in els_minor: 206 if el in els_major: 207 continue # draw only once as major 208 draw_iso_elevation_circle(el) 209 gl.glEnd() 210 gl.glEndList() 211 212 gl.glNewList(self.cached_major_lines_display_list,gl.GL_COMPILE) 213 gl.glBegin(gl.GL_LINES) 214 for az in azs_major: 215 draw_half_great_circle(az) 216 for el in els_major: 217 draw_iso_elevation_circle(el) 218 gl.glEnd() 219 gl.glEndList() 220 221 if cp.use_text: 222 self.labels = [] 223 self.labels_xyz = [] 224 els_major = list(els_major)+[90.0] # make sure we have north pole 225 for el in els_major: 226 for az in azs_major: 227 theta = -(el-90) / 180.0 * math.pi 228 phi = (az-90.0)/180.0*math.pi 229 x,y,z = get_xyz(theta,phi,cp.radius) 230 self.labels_xyz.append((x,y,z)) 231 self.labels.append( 232 VisionEgg.Text.Text( text = '%.0f, %.0f'%(az,el), 233 font_size = cp.font_size, 234 color = cp.text_color, 235 anchor = cp.text_anchor, 236 ) 237 ) 238 if (el == -90) or (el == 90): 239 self.labels[-1].parameters.text = 'x, %.0f'%(el,) 240 break # only one label at the poles 241 242 self.labels_xyz = Numeric.array(self.labels_xyz) 243
244 - def draw(self):
245 p = self.parameters 246 cp = self.constant_parameters 247 if p.on: 248 # Set OpenGL state variables 249 gl.glDisable( gl.GL_DEPTH_TEST ) 250 gl.glDisable( gl.GL_TEXTURE_2D ) # Make sure textures are not drawn 251 gl.glMatrixMode(gl.GL_MODELVIEW) 252 gl.glPushMatrix() 253 gl.glRotatef(p.center_azimuth,0.0,-1.0,0.0) 254 gl.glRotatef(p.center_elevation,1.0,0.0,0.0) 255 256 if p.anti_aliasing: 257 if len(p.minor_line_color) == 4 and not self._gave_alpha_warning: 258 if p.minor_line_color[3] != 1.0: 259 logger = logging.getLogger('VisionEgg.SphereMap') 260 logger.warning("The parameter anti_aliasing is " 261 "set to true in the AzElGrid " 262 "stimulus class, but the color " 263 "parameter specifies an alpha " 264 "value other than 1.0. To " 265 "acheive the best anti-aliasing, " 266 "ensure that the alpha value for " 267 "the color parameter is 1.0.") 268 self._gave_alpha_warning = 1 269 if len(p.major_line_color) == 4 and not self._gave_alpha_warning: 270 if p.major_line_color[3] != 1.0: 271 logger = logging.getLogger('VisionEgg.SphereMap') 272 logger.warning("The parameter anti_aliasing is " 273 "set to true in the AzElGrid " 274 "stimulus class, but the color " 275 "parameter specifies an alpha " 276 "value other than 1.0. To " 277 "acheive the best anti-aliasing, " 278 "ensure that the alpha value for " 279 "the color parameter is 1.0.") 280 self._gave_alpha_warning = 1 281 gl.glEnable( gl.GL_LINE_SMOOTH ) 282 # allow max_alpha value to control blending 283 gl.glEnable( gl.GL_BLEND ) 284 gl.glBlendFunc( gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA ) 285 else: 286 gl.glDisable( gl.GL_BLEND ) 287 288 if len(p.minor_line_color)==3: 289 gl.glColor3f(*p.minor_line_color) 290 elif len(p.minor_line_color)==4: 291 gl.glColor4f(*p.minor_line_color) 292 gl.glLineWidth(p.minor_line_width) 293 gl.glCallList(self.cached_minor_lines_display_list) 294 295 if len(p.major_line_color)==3: 296 gl.glColor3f(*p.major_line_color) 297 elif len(p.major_line_color)==4: 298 gl.glColor4f(*p.major_line_color) 299 gl.glLineWidth(p.major_line_width) 300 gl.glCallList(self.cached_major_lines_display_list) 301 302 if p.anti_aliasing: 303 gl.glDisable( gl.GL_LINE_SMOOTH ) # turn off 304 305 if cp.use_text: 306 my_view = p.my_viewport 307 if (my_view is None) or (not my_view._is_drawing): 308 raise ValueError('use_text is True, but my_viewport not (properly) assigned') 309 310 if self.text_viewport is None or self.text_viewport_orig != my_view: 311 # make viewport for text (uses default orthographic projection) 312 vp = my_view.parameters 313 self.text_viewport = VisionEgg.Core.Viewport(screen=vp.screen, 314 position=vp.position, 315 size=vp.size, 316 anchor=vp.anchor, 317 ) 318 lowerleft = VisionEgg._get_lowerleft(vp.position,vp.anchor,vp.size) 319 self.text_viewport.parameters.projection.stateless_translate(-lowerleft[0],-lowerleft[1],0) 320 self.text_viewport_orig = p.my_viewport # in case my_viewport changes, change text_viewport 321 322 # draw text labels 323 my_proj = my_view.parameters.projection 324 325 xyz = self.labels_xyz 326 327 t = VisionEgg.ThreeDeeMath.TransformMatrix() 328 t.rotate( p.center_azimuth,0.0,-1.0,0.0 ) # acheive same transforms as the lines 329 t.rotate( p.center_elevation,1.0,0.0,0.0 ) 330 331 xyz = t.transform_vertices(self.labels_xyz) 332 333 clip = my_proj.eye_2_clip(xyz) 334 try: 335 # this is much faster when no OverflowError... 336 window_coords = my_view.clip_2_window(clip) 337 all_at_once = True 338 except OverflowError: 339 all_at_once = False 340 draw_labels = [] 341 for i in range(len(self.labels)): 342 if clip[i,3] < 0: continue # this vertex is not on screen 343 label = self.labels[i] 344 if all_at_once: 345 this_pos = window_coords[i,:2] 346 else: 347 try: 348 window_coords = my_view.clip_2_window(clip[i,:]) 349 except OverflowError: 350 continue # not much we can do with this vertex, either 351 this_pos = window_coords[:2] 352 label.parameters.position = (this_pos[0] + p.text_offset[0], 353 this_pos[1] + p.text_offset[1]) 354 draw_labels.append(label) 355 self.text_viewport.parameters.stimuli = draw_labels 356 self.text_viewport.draw() 357 my_view.make_current() # restore viewport 358 gl.glPopMatrix()
359
360 -class SphereMap(VisionEgg.Textures.TextureStimulusBaseClass):
361 """Mercator mapping of rectangular texture onto sphere. 362 363 Parameters 364 ========== 365 center_azimuth -- (Real) 366 Default: 0.0 367 center_elevation -- (Real) 368 Default: 0.0 369 contrast -- (Real) 370 Default: 1.0 371 on -- (Boolean) 372 Default: True 373 radius -- (Real) 374 Default: 1.0 375 slices -- (UnsignedInteger) 376 Default: 30 377 stacks -- (UnsignedInteger) 378 Default: 30 379 texture -- source of texture data (Instance of <class 'VisionEgg.Textures.Texture'>) 380 Inherited from VisionEgg.Textures.TextureStimulusBaseClass 381 Default: (determined at runtime) 382 texture_mag_filter -- OpenGL filter enum (Integer) 383 Inherited from VisionEgg.Textures.TextureStimulusBaseClass 384 Default: GL_LINEAR (9729) 385 texture_min_filter -- OpenGL filter enum (Integer) 386 Inherited from VisionEgg.Textures.TextureStimulusBaseClass 387 Default: (GL enum determined at runtime) 388 texture_wrap_s -- OpenGL texture wrap enum (Integer) 389 Inherited from VisionEgg.Textures.TextureStimulusBaseClass 390 Default: (GL enum determined at runtime) 391 texture_wrap_t -- OpenGL texture wrap enum (Integer) 392 Inherited from VisionEgg.Textures.TextureStimulusBaseClass 393 Default: (GL enum determined at runtime) 394 395 Constant Parameters 396 =================== 397 internal_format -- format with which OpenGL uses texture data (OpenGL data type enum) (Integer) 398 Default: GL_RGB (6407) 399 mipmaps_enabled -- Are mipmaps enabled? (Boolean) 400 Default: True 401 shrink_texture_ok -- Allow automatic shrinking of texture if too big? (Boolean) 402 Default: False 403 """ 404 405 parameters_and_defaults = { 406 'on':(True, 407 ve_types.Boolean), 408 'contrast':(1.0, 409 ve_types.Real), 410 'center_azimuth':(0.0, # 0=right, 90=right 411 ve_types.Real), 412 'center_elevation':(0.0, # 0=right, 90=up 413 ve_types.Real), 414 415 # Changing these parameters will cause re-computation of display list (may cause frame skip) 416 'radius':(1.0, 417 ve_types.Real), 418 'slices':(30, 419 ve_types.UnsignedInteger), 420 'stacks':(30, 421 ve_types.UnsignedInteger)} 422 423 __slots__ = ( 424 'cached_display_list', 425 '_cached_radius', 426 '_cached_slices', 427 '_cached_stacks', 428 ) 429
430 - def __init__(self,**kw):
431 VisionEgg.Textures.TextureStimulusBaseClass.__init__(self,**kw) 432 self.cached_display_list = gl.glGenLists(1) # Allocate a new display list 433 self.__rebuild_display_list()
434
435 - def __rebuild_display_list(self):
436 p = self.parameters 437 438 s_gain = p.texture.buf_rf - p.texture.buf_lf 439 t_gain = p.texture.buf_bf - p.texture.buf_tf 440 441 s_offs = p.texture.buf_lf 442 t_offs = p.texture.buf_tf 443 444 gl.glNewList(self.cached_display_list,gl.GL_COMPILE) 445 gl.glBegin(gl.GL_QUADS) 446 447 for stack in range(p.stacks): 448 stack_upper_frac = float(stack+1)/p.stacks 449 stack_lower_frac = float(stack)/p.stacks 450 theta_upper = stack_upper_frac * math.pi 451 theta_lower = stack_lower_frac * math.pi 452 y_upper = p.radius * math.cos( theta_upper ) 453 w_upper = p.radius * math.sin( theta_upper ) 454 y_lower = p.radius * math.cos( theta_lower ) 455 w_lower = p.radius * math.sin( theta_lower ) 456 for slice in range(p.slices): 457 slice_start_frac = float(slice)/p.slices 458 slice_stop_frac = float(slice+1)/p.slices 459 phi_start = slice_start_frac * 2 * math.pi 460 phi_stop = slice_stop_frac * 2 * math.pi 461 x_start_upper = w_upper * math.cos(phi_start) 462 x_start_lower = w_lower * math.cos(phi_start) 463 x_stop_upper = w_upper * math.cos(phi_stop) 464 x_stop_lower = w_lower * math.cos(phi_stop) 465 z_start_upper = w_upper * math.sin(phi_start) 466 z_start_lower = w_lower * math.sin(phi_start) 467 z_stop_upper = w_upper * math.sin(phi_stop) 468 z_stop_lower = w_lower * math.sin(phi_stop) 469 470 tex_l = slice_start_frac*s_gain+s_offs 471 tex_r = slice_stop_frac*s_gain+s_offs 472 tex_b = stack_lower_frac*t_gain+t_offs 473 tex_t = stack_upper_frac*t_gain+t_offs 474 475 gl.glTexCoord2f(tex_l,tex_t) 476 gl.glVertex3f(x_start_upper, y_upper, z_start_upper) 477 478 gl.glTexCoord2f(tex_r,tex_t) 479 gl.glVertex3f(x_stop_upper, y_upper, z_stop_upper) 480 481 gl.glTexCoord2f(tex_r,tex_b) 482 gl.glVertex3f(x_stop_lower, y_lower, z_stop_lower) 483 484 gl.glTexCoord2f(tex_l,tex_b) 485 gl.glVertex3f(x_start_lower, y_lower, z_start_lower) 486 487 gl.glEnd() 488 gl.glEndList() 489 self._cached_radius = p.radius 490 self._cached_slices = p.slices 491 self._cached_stacks = p.stacks
492
493 - def draw(self):
494 """Redraw the scene on every frame. 495 """ 496 p = self.parameters 497 498 if self._cached_radius != p.radius or self._cached_slices != p.slices or self._cached_stacks != p.stacks: 499 self.__rebuild_display_list() 500 501 if p.on: 502 # Set OpenGL state variables 503 gl.glEnable( gl.GL_DEPTH_TEST ) 504 gl.glEnable( gl.GL_TEXTURE_2D ) # Make sure textures are drawn 505 gl.glEnable( gl.GL_BLEND ) # Contrast control implemented through blending 506 507 # All of the contrast control stuff is somewhat arcane and 508 # not very clear from reading the code, so here is how it 509 # works in English. (Not that it makes it any more clear!) 510 # 511 # In the final "textured fragment" (before being blended 512 # to the framebuffer), the color values are equal to those 513 # of the texture (with the exception of pixels around the 514 # edges which have their amplitudes reduced due to 515 # anti-aliasing and are intermediate between the color of 516 # the texture and mid-gray), and the alpha value is set to 517 # the contrast. Blending occurs, and by choosing the 518 # appropriate values for glBlendFunc, adds the product of 519 # fragment alpha (contrast) and fragment color to the 520 # product of one minus fragment alpha (contrast) and what 521 # was already in the framebuffer. 522 523 gl.glBlendFunc( gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA ) 524 525 gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_DECAL) 526 527 # clear modelview matrix 528 gl.glMatrixMode(gl.GL_MODELVIEW) 529 gl.glPushMatrix() 530 gl.glColor4f(0.5,0.5,0.5,p.contrast) # Set the polygons' fragment color (implements contrast) 531 532 if not self.constant_parameters.mipmaps_enabled: 533 if p.texture_min_filter in VisionEgg.Textures.TextureStimulusBaseClass._mipmap_modes: 534 raise RuntimeError("Specified a mipmap mode in texture_min_filter, but mipmaps not enabled.") 535 self.texture_object.set_min_filter( p.texture_min_filter ) 536 self.texture_object.set_mag_filter( p.texture_mag_filter ) 537 self.texture_object.set_wrap_mode_s( p.texture_wrap_s ) 538 self.texture_object.set_wrap_mode_t( p.texture_wrap_t ) 539 540 # center the texture map 541 gl.glRotatef(p.center_azimuth,0.0,-1.0,0.0) 542 gl.glRotatef(p.center_elevation,1.0,0.0,0.0) 543 544 gl.glCallList(self.cached_display_list) 545 gl.glPopMatrix()
546
547 -class SphereGrating(VisionEgg.Gratings.LuminanceGratingCommon):
548 """Map 2D sinusoidal grating onto sphere. 549 550 Parameters 551 ========== 552 bit_depth -- precision with which grating is calculated and sent to OpenGL (UnsignedInteger) 553 Inherited from VisionEgg.Gratings.LuminanceGratingCommon 554 Default: 8 555 check_texture_size -- (Boolean) 556 Default: True 557 contrast -- (Real) 558 Default: 1.0 559 grating_center_azimuth -- (Real) 560 Default: 0.0 561 grating_center_elevation -- (Real) 562 Default: 0.0 563 ignore_time -- (Boolean) 564 Default: False 565 lowpass_cutoff_cycles_per_texel -- helps prevent spatial aliasing (Real) 566 Default: 0.5 567 min_filter -- OpenGL filter enum (Integer) 568 Default: GL_LINEAR (9729) 569 num_samples -- (UnsignedInteger) 570 Default: 1024 571 on -- (Boolean) 572 Default: True 573 orientation -- (Real) 574 Default: 0.0 575 phase_at_t0 -- (Real) 576 Default: 0.0 577 radius -- (Real) 578 Default: 1.0 579 slices -- (UnsignedInteger) 580 Default: 30 581 spatial_freq_cpd -- (Real) 582 Default: 0.0277777777778 583 stacks -- (UnsignedInteger) 584 Default: 30 585 t0_time_sec_absolute -- (Real) 586 Default: (determined at runtime) 587 temporal_freq_hz -- (Real) 588 Default: 5.0 589 """ 590 591 parameters_and_defaults = { 592 'on':(True, 593 ve_types.Boolean), 594 'contrast':(1.0, 595 ve_types.Real), 596 'spatial_freq_cpd':(1.0/36.0, # cycles/degree 597 ve_types.Real), 598 'temporal_freq_hz':(5.0, # hz 599 ve_types.Real), 600 't0_time_sec_absolute':(None, 601 ve_types.Real), 602 'ignore_time':(False, # ignore temporal frequency variable - allow control purely with phase_at_t0 603 ve_types.Boolean), 604 'phase_at_t0':(0.0, # degrees 605 ve_types.Real), 606 'orientation':(0.0, # 0=right, 90=up 607 ve_types.Real), 608 'grating_center_azimuth':(0.0, # 0=right, 90=down 609 ve_types.Real), 610 'grating_center_elevation':(0.0, # 0=right, 90=down 611 ve_types.Real), 612 'check_texture_size':(True, # slows down drawing but catches errors 613 ve_types.Boolean), 614 'lowpass_cutoff_cycles_per_texel':(0.5, 615 ve_types.Real, 616 'helps prevent spatial aliasing'), 617 'min_filter':(gl.GL_LINEAR, 618 ve_types.Integer, 619 "OpenGL filter enum", 620 VisionEgg.ParameterDefinition.OPENGL_ENUM), 621 # changing this parameters causes re-drawing of the texture object and may cause frame skipping 622 'num_samples':(1024, # number of spatial samples, should be a power of 2 623 ve_types.UnsignedInteger), 624 # Changing these parameters will cause re-computation of display list (may cause frame skip) 625 'radius':(1.0, 626 ve_types.Real), 627 'slices':(30, 628 ve_types.UnsignedInteger), 629 'stacks':(30, 630 ve_types.UnsignedInteger), 631 } 632 633 __slots__ = ( 634 'texture_object_id', 635 'cached_display_list_id', 636 '_cached_num_samples', 637 '_cached_radius', 638 '_cached_slices', 639 '_cached_stacks', 640 ) 641
642 - def __init__(self,**kw):
643 VisionEgg.Gratings.LuminanceGratingCommon.__init__(self,**kw) 644 645 if self.parameters.t0_time_sec_absolute is None: 646 self.parameters.t0_time_sec_absolute = VisionEgg.time_func() 647 648 self.texture_object_id = gl.glGenTextures(1) # Allocate a new texture object 649 self.__rebuild_texture_object() 650 651 self.cached_display_list_id = gl.glGenLists(1) # Allocate a new display list 652 self.__rebuild_display_list()
653
654 - def __rebuild_texture_object(self):
655 gl.glBindTexture(gl.GL_TEXTURE_1D,self.texture_object_id) 656 p = self.parameters # shorthand 657 658 # Do error-checking on texture to make sure it will load 659 max_dim = gl.glGetIntegerv(gl.GL_MAX_TEXTURE_SIZE) 660 if p.num_samples > max_dim: 661 raise VisionEgg.Gratings.NumSamplesTooLargeError("Grating num_samples too large for video system.\nOpenGL reports maximum size of %d"%(max_dim,)) 662 663 self.calculate_bit_depth_dependencies() 664 665 l = 0.0 666 r = 360.0 667 668 mipmap_level = 0 669 this_mipmap_level_num_samples = p.num_samples 670 while this_mipmap_level_num_samples >= 1: 671 inc = 360.0/float(this_mipmap_level_num_samples) # degrees per pixel 672 cycles_per_texel = p.spatial_freq_cpd * inc 673 if cycles_per_texel < p.lowpass_cutoff_cycles_per_texel: # sharp cutoff lowpass filter 674 # below cutoff frequency - draw sine wave 675 if p.ignore_time: 676 phase = p.phase_at_t0 677 else: 678 t_var = VisionEgg.time_func() - p.t0_time_sec_absolute 679 phase = t_var*p.temporal_freq_hz*360.0 + p.phase_at_t0 680 floating_point_sin = Numeric.sin(2.0*math.pi*p.spatial_freq_cpd*Numeric.arange(l,r,inc,'d')-(phase/180.0*math.pi))*0.5*p.contrast+0.5 681 floating_point_sin = Numeric.clip(floating_point_sin,0.0,1.0) # allow square wave generation if contrast > 1 682 texel_data = (floating_point_sin*self.max_int_val).astype(self.numpy_dtype).tostring() 683 else: 684 # above cutoff frequency - blank 685 texel_data = (self.max_int_val*0.5)*Numeric.ones((this_mipmap_level_num_samples,),self.numpy_dtype) 686 687 if p.check_texture_size: 688 # Because the MAX_TEXTURE_SIZE method is insensitive to the current 689 # state of the video system, another check must be done using 690 # "proxy textures". 691 gl.glTexImage1D(gl.GL_PROXY_TEXTURE_1D, # target 692 mipmap_level, # level 693 self.gl_internal_format, # video RAM internal format: RGB 694 this_mipmap_level_num_samples, # width 695 0, # border 696 self.format, # format of image data 697 self.gl_type, # type of image data 698 texel_data) # texel data 699 if gl.glGetTexLevelParameteriv(gl.GL_PROXY_TEXTURE_1D,0,gl.GL_TEXTURE_WIDTH) == 0: 700 raise NumSamplesTooLargeError("Grating num_samples is too wide for your video system!") 701 702 # If we got here, it worked and we can load the texture for real. 703 gl.glTexImage1D(gl.GL_TEXTURE_1D, # target 704 mipmap_level, # level 705 self.gl_internal_format, # video RAM internal format: RGB 706 this_mipmap_level_num_samples, # width 707 0, # border 708 self.format, # format of image data 709 self.gl_type, # type of image data 710 texel_data) # texel data 711 712 # prepare for next mipmap level 713 this_mipmap_level_num_samples = this_mipmap_level_num_samples/2 # integer division 714 mipmap_level += 1 715 716 # Set some texture object defaults 717 gl.glTexParameteri(gl.GL_TEXTURE_1D,gl.GL_TEXTURE_WRAP_S,gl.GL_REPEAT) 718 gl.glTexParameteri(gl.GL_TEXTURE_1D,gl.GL_TEXTURE_WRAP_T,gl.GL_REPEAT) 719 gl.glTexParameteri(gl.GL_TEXTURE_1D,gl.GL_TEXTURE_MAG_FILTER,gl.GL_LINEAR) 720 gl.glTexParameteri(gl.GL_TEXTURE_1D,gl.GL_TEXTURE_MIN_FILTER,p.min_filter) 721 self._cached_num_samples = p.num_samples
722
723 - def __rebuild_display_list(self):
724 gl.glNewList(self.cached_display_list_id,gl.GL_COMPILE) 725 726 p = self.parameters 727 gl.glBegin(gl.GL_QUADS) 728 729 for stack in range(p.stacks): 730 stack_upper_frac = float(stack+1)/p.stacks 731 stack_lower_frac = float(stack)/p.stacks 732 theta_upper = stack_upper_frac * math.pi 733 theta_lower = stack_lower_frac * math.pi 734 y_upper = p.radius * math.cos( theta_upper ) 735 w_upper = p.radius * math.sin( theta_upper ) 736 y_lower = p.radius * math.cos( theta_lower ) 737 w_lower = p.radius * math.sin( theta_lower ) 738 for slice in range(p.slices): 739 slice_start_frac = float(slice)/p.slices 740 slice_stop_frac = float(slice+1)/p.slices 741 phi_start = slice_start_frac * 2 * math.pi 742 phi_stop = slice_stop_frac * 2 * math.pi 743 x_start_upper = w_upper * math.cos(phi_start) 744 x_start_lower = w_lower * math.cos(phi_start) 745 x_stop_upper = w_upper * math.cos(phi_stop) 746 x_stop_lower = w_lower * math.cos(phi_stop) 747 z_start_upper = w_upper * math.sin(phi_start) 748 z_start_lower = w_lower * math.sin(phi_start) 749 z_stop_upper = w_upper * math.sin(phi_stop) 750 z_stop_lower = w_lower * math.sin(phi_stop) 751 752 tex_l = slice_start_frac 753 tex_r = slice_stop_frac 754 tex_b = 0.0#stack_lower_frac 755 tex_t = 1.0#stack_upper_frac 756 757 gl.glTexCoord2f(tex_l,tex_t) 758 gl.glVertex3f(x_start_upper, y_upper, z_start_upper) 759 760 gl.glTexCoord2f(tex_r,tex_t) 761 gl.glVertex3f(x_stop_upper, y_upper, z_stop_upper) 762 763 gl.glTexCoord2f(tex_r,tex_b) 764 gl.glVertex3f(x_stop_lower, y_lower, z_stop_lower) 765 766 gl.glTexCoord2f(tex_l,tex_b) 767 gl.glVertex3f(x_start_lower, y_lower, z_start_lower) 768 769 gl.glEnd() 770 gl.glEndList() 771 self._cached_radius = p.radius 772 self._cached_slices = p.slices 773 self._cached_stacks = p.stacks
774
775 - def draw(self):
776 """Redraw the scene on every frame. 777 """ 778 p = self.parameters 779 780 if self._cached_radius != p.radius or self._cached_slices != p.slices or self._cached_stacks != p.stacks: 781 self.__rebuild_display_list() 782 783 if self._cached_num_samples != p.num_samples: 784 self.__rebuild_texture_object() 785 786 if p.on: 787 if p.bit_depth != self.cached_bit_depth: 788 self.calculate_bit_depth_dependencies() 789 # Set OpenGL state variables 790 gl.glEnable( gl.GL_DEPTH_TEST ) 791 gl.glEnable( gl.GL_TEXTURE_1D ) # Make sure textures are drawn 792 gl.glDisable( gl.GL_TEXTURE_2D ) 793 gl.glDisable( gl.GL_BLEND ) 794 795 gl.glBindTexture(gl.GL_TEXTURE_1D,self.texture_object_id) 796 gl.glTexParameteri(gl.GL_TEXTURE_1D,gl.GL_TEXTURE_MIN_FILTER,p.min_filter) 797 798 l = 0.0 799 r = 360.0 800 801 mipmap_level = 0 802 this_mipmap_level_num_samples = p.num_samples 803 while this_mipmap_level_num_samples >= 1: 804 inc = 360.0/float(this_mipmap_level_num_samples)# degrees per pixel 805 cycles_per_texel = p.spatial_freq_cpd * inc 806 if cycles_per_texel < p.lowpass_cutoff_cycles_per_texel: # sharp cutoff lowpass filter 807 if p.ignore_time: 808 phase = p.phase_at_t0 809 else: 810 t_var = VisionEgg.time_func() - p.t0_time_sec_absolute 811 phase = t_var*p.temporal_freq_hz*360.0 + p.phase_at_t0 812 floating_point_sin = Numeric.sin(2.0*math.pi*p.spatial_freq_cpd*Numeric.arange(l,r,inc,'d')-(phase/180.0*math.pi))*0.5*p.contrast+0.5 813 floating_point_sin = Numeric.clip(floating_point_sin,0.0,1.0) # allow square wave generation if contrast > 1 814 texel_data = (floating_point_sin*self.max_int_val).astype(self.numpy_dtype).tostring() 815 else: 816 blank = 0.5*Numeric.ones((this_mipmap_level_num_samples,),'d') 817 texel_data = (blank*self.max_int_val).astype(self.numpy_dtype).tostring() 818 819 gl.glTexSubImage1D(gl.GL_TEXTURE_1D, # target 820 mipmap_level, # level 821 0, # x offset 822 this_mipmap_level_num_samples, # width 823 self.format, # data format 824 self.gl_type, # data type 825 texel_data) 826 827 # prepare for next mipmap level 828 this_mipmap_level_num_samples = this_mipmap_level_num_samples/2 # integer division 829 mipmap_level += 1 830 831 gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_REPLACE) 832 833 # clear modelview matrix 834 gl.glMatrixMode(gl.GL_MODELVIEW) 835 gl.glPushMatrix() 836 # center the grating 837 gl.glRotatef(p.grating_center_azimuth,0.0,-1.0,0.0) 838 gl.glRotatef(p.grating_center_elevation,1.0,0.0,0.0) 839 840 # do the orientation 841 gl.glRotatef(p.orientation,0.0,0.0,1.0) 842 843 gl.glCallList(self.cached_display_list_id) 844 845 gl.glDisable( gl.GL_TEXTURE_1D ) 846 gl.glPopMatrix()
847
848 -class SphereWindow(VisionEgg.Gratings.LuminanceGratingCommon):
849 """This draws an opaque sphere with a single window in it. 850 851 This is useful when you need to have a viewport on a 3D scene. 852 853 Parameters 854 ========== 855 bit_depth -- precision with which grating is calculated and sent to OpenGL (UnsignedInteger) 856 Inherited from VisionEgg.Gratings.LuminanceGratingCommon 857 Default: 8 858 num_s_samples -- (UnsignedInteger) 859 Default: 512 860 num_t_samples -- (UnsignedInteger) 861 Default: 512 862 on -- (Boolean) 863 Default: True 864 opaque_color -- (Sequence4 of Real) 865 Default: (0.5, 0.5, 0.5, 0.0) 866 radius -- (Real) 867 Default: 1.0 868 slices -- (UnsignedInteger) 869 Default: 30 870 stacks -- (UnsignedInteger) 871 Default: 30 872 window_center_azimuth -- (Real) 873 Default: 0.0 874 window_center_elevation -- (Real) 875 Default: 0.0 876 window_shape -- can be 'circle', 'gaussian', or 'lat-long rectangle' (String) 877 Default: gaussian 878 window_shape_parameter2 -- (currently only used for) height of lat-long rectangle (in degrees) (Real) 879 Default: 30.0 880 window_shape_radius_parameter -- radius of circle, sigma of gaussian, width of lat-long rectangle (in degrees) (Real) 881 Default: 36.0 882 """ 883 884 parameters_and_defaults = { 885 'on':(True, 886 ve_types.Boolean), 887 'window_center_elevation':(0.0, 888 ve_types.Real), 889 'window_center_azimuth':(0.0, 890 ve_types.Real), 891 'opaque_color':((0.5,0.5,0.5,0.0), 892 ve_types.Sequence4(ve_types.Real)), 893 # changing these parameters causes re-drawing of the texture object and may cause frame skipping 894 'window_shape':('gaussian', # can be 'circle' or 'gaussian' 895 ve_types.String, 896 "can be 'circle', 'gaussian', or 'lat-long rectangle'", 897 ), 898 'window_shape_radius_parameter':(36.0, 899 ve_types.Real, 900 'radius of circle, sigma of gaussian, width of lat-long rectangle (in degrees)', 901 ), 902 'window_shape_parameter2':(30.0, 903 ve_types.Real, 904 '(currently only used for) height of lat-long rectangle (in degrees)', 905 ), 906 'num_s_samples':(512, # number of horizontal spatial samples, should be a power of 2 907 ve_types.UnsignedInteger), 908 'num_t_samples':(512, # number of vertical spatial samples, should be a power of 2 909 ve_types.UnsignedInteger), 910 # Changing these parameters will cause re-computation of display list (may cause frame skip) 911 'radius':(1.0, # XXX could modify code below to use scaling, thus avoiding need for recomputation 912 ve_types.Real), 913 'slices':(30, 914 ve_types.UnsignedInteger), 915 'stacks':(30, 916 ve_types.UnsignedInteger), 917 } 918 919 __slots__ = ( 920 'texture_object_id', 921 'windowed_display_list_id', 922 'opaque_display_list_id', 923 '_cached_window_shape', 924 '_cached_shape_radius_parameter', 925 '_cached_shape_parameter2', 926 '_cached_num_s_samples', 927 '_cached_num_t_samples', 928 '_cached_radius', 929 '_cached_slices', 930 '_cached_stacks', 931 '_texture_s_is_azimuth', 932 ) 933
934 - def __init__(self, **kw):
935 VisionEgg.Gratings.LuminanceGratingCommon.__init__(self, **kw ) 936 937 p = self.parameters 938 939 # set self._texture_s_is_azimuth in advance 940 if p.window_shape == 'lat-long rectangle': 941 self._texture_s_is_azimuth = True 942 else: 943 self._texture_s_is_azimuth = False 944 945 self.texture_object_id = gl.glGenTextures(1) 946 self.__rebuild_texture_object() 947 948 self.windowed_display_list_id = gl.glGenLists(1) # Allocate a new display list 949 self.opaque_display_list_id = gl.glGenLists(1) # Allocate a new display list 950 self.__rebuild_display_lists()
951
952 - def __rebuild_texture_object(self):
953 gl.glBindTexture(gl.GL_TEXTURE_2D,self.texture_object_id) 954 p = self.parameters 955 956 # Do error-checking on texture to make sure it will load 957 max_dim = gl.glGetIntegerv(gl.GL_MAX_TEXTURE_SIZE) 958 if p.num_s_samples > max_dim: 959 raise VisionEgg.Gratings.NumSamplesTooLargeError("SphereWindow num_s_samples too large for video system.\nOpenGL reports maximum size of %d"%(max_dim,)) 960 if p.num_t_samples > max_dim: 961 raise VisionEgg.Gratings.NumSamplesTooLargeError("SphereWindow num_t_samples too large for video system.\nOpenGL reports maximum size of %d"%(max_dim,)) 962 963 self.calculate_bit_depth_dependencies() 964 self.gl_internal_format = gl.GL_ALPHA # change from luminance to alpha 965 self.format = gl.GL_ALPHA 966 967 # texture coordinates are Mercator: (determined when building display list) 968 # s: x within sphere 969 # t: z within sphere 970 971 if p.window_shape == 'circle': 972 if self._texture_s_is_azimuth: 973 self.__rebuild_display_lists() 974 975 # XXX this is aliased 976 s_axis = (Numeric.arange(p.num_s_samples)/float(p.num_s_samples)-0.5)**2 977 t_axis = (Numeric.arange(p.num_t_samples)/float(p.num_t_samples)-0.5)**2 978 mask = s_axis[Numeric.NewAxis,:] + t_axis[:,Numeric.NewAxis] 979 angle_deg = min(180,p.window_shape_radius_parameter) # clip angle 980 cartesian_radius = 0.5*math.sin(p.window_shape_radius_parameter/180.0*math.pi) 981 floating_point_window = Numeric.less(mask,cartesian_radius**2) 982 elif p.window_shape == 'gaussian': 983 if self._texture_s_is_azimuth: 984 self.__rebuild_display_lists() 985 986 MIN_EXP = -745.0 987 MAX_EXP = 709.0 988 989 s = Numeric.arange(0.0,p.num_s_samples,1.0,'f')/p.num_s_samples 990 t = Numeric.arange(0.0,p.num_t_samples,1.0,'f')/p.num_t_samples 991 sigma_normalized = p.window_shape_radius_parameter / 90.0 * 0.5 992 993 check_s = -((s-0.5)**2/(2.0*sigma_normalized**2)) 994 try: 995 # some platforms raise OverflowError when doing this on small numbers 996 val_s = Numeric.exp( check_s ) 997 except OverflowError: 998 check_s = Numeric.clip(check_s,MIN_EXP,MAX_EXP) 999 val_s = Numeric.exp( check_s ) 1000 1001 check_t = -((t-0.5)**2/(2.0*sigma_normalized**2)) 1002 try: 1003 val_t = Numeric.exp( check_t ) 1004 except OverflowError: 1005 check_t = Numeric.clip(check_t,MIN_EXP,MAX_EXP) 1006 val_t = Numeric.exp( check_t ) 1007 floating_point_window = Numeric.outerproduct(val_t,val_s) 1008 elif p.window_shape == 'lat-long rectangle': 1009 if not self._texture_s_is_azimuth: 1010 self.__rebuild_display_lists() 1011 1012 # s coordinate represents -90 to +90 degrees (azimuth). 1013 s_axis = (Numeric.arange(p.num_s_samples)/float(p.num_s_samples)-0.5)*180 1014 s_axis = Numeric.less( abs(s_axis), p.window_shape_radius_parameter*0.5 ) 1015 1016 # t coordinate represents height. 1017 # Convert angle to height. 1018 angle_deg = min(90,p.window_shape_parameter2*0.5) # clip angle 1019 desired_height = math.sin(angle_deg/180.0*math.pi)*0.5 1020 t_axis = Numeric.arange(p.num_t_samples)/float(p.num_t_samples)-0.5 1021 t_axis = Numeric.less(abs(t_axis),desired_height) 1022 floating_point_window = Numeric.outerproduct(t_axis,s_axis) 1023 else: 1024 raise RuntimeError('Unknown window_shape "%s"'%(p.window_shape,)) 1025 texel_data = (floating_point_window * self.max_int_val).astype(self.numpy_dtype).tostring() 1026 1027 # Because the MAX_TEXTURE_SIZE method is insensitive to the current 1028 # state of the video system, another check must be done using 1029 # "proxy textures". 1030 gl.glTexImage2D(gl.GL_PROXY_TEXTURE_2D, # target 1031 0, # mipmap_level 1032 self.gl_internal_format, # video RAM internal format 1033 p.num_s_samples, # width 1034 p.num_t_samples, # height 1035 0, # border 1036 self.format, # format of image data 1037 self.gl_type, # type of image data 1038 texel_data) # texel data 1039 if (gl.glGetTexLevelParameteriv(gl.GL_PROXY_TEXTURE_2D, # Need PyOpenGL >= 2.0 1040 0, 1041 gl.GL_TEXTURE_WIDTH) == 0) or ( 1042 gl.glGetTexLevelParameteriv(gl.GL_PROXY_TEXTURE_2D, 1043 0, 1044 gl.GL_TEXTURE_HEIGHT) == 0): 1045 raise VisionEgg.Gratings.NumSamplesTooLargeError("SphereWindow num_s_samples or num_t_samples is too large for your video system!") 1046 1047 gl.glTexImage2D(gl.GL_TEXTURE_2D, # target 1048 0, # mipmap_level 1049 self.gl_internal_format, # video RAM internal format 1050 p.num_s_samples, # width 1051 p.num_t_samples, # height 1052 0, # border 1053 self.format, # format of image data 1054 self.gl_type, # type of image data 1055 texel_data) # texel data 1056 1057 # Set some texture object defaults 1058 gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_WRAP_S,gl.GL_CLAMP_TO_EDGE) 1059 gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_WRAP_T,gl.GL_CLAMP_TO_EDGE) 1060 gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_MAG_FILTER,gl.GL_LINEAR) 1061 gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_MIN_FILTER,gl.GL_LINEAR) 1062 1063 self._cached_window_shape = p.window_shape 1064 self._cached_shape_radius_parameter = p.window_shape_radius_parameter 1065 self._cached_shape_parameter2 = p.window_shape_parameter2 1066 self._cached_num_s_samples = p.num_s_samples 1067 self._cached_num_t_samples = p.num_t_samples
1068
1069 - def __rebuild_display_lists(self):
1070 gl.glMatrixMode(gl.GL_MODELVIEW) 1071 gl.glPushMatrix() 1072 1073 p = self.parameters 1074 1075 if p.window_shape == 'lat-long rectangle': 1076 self._texture_s_is_azimuth = True 1077 else: 1078 self._texture_s_is_azimuth = False 1079 1080 gl.glNewList(self.windowed_display_list_id,gl.GL_COMPILE) 1081 1082 gl.glBegin(gl.GL_QUADS) 1083 1084 for stack in range(p.stacks): 1085 stack_upper_frac = float(stack+1)/p.stacks 1086 stack_lower_frac = float(stack)/p.stacks 1087 theta_upper = stack_upper_frac * math.pi 1088 theta_lower = stack_lower_frac * math.pi 1089 y_upper = p.radius * math.cos( theta_upper ) 1090 w_upper = p.radius * math.sin( theta_upper ) 1091 y_lower = p.radius * math.cos( theta_lower ) 1092 w_lower = p.radius * math.sin( theta_lower ) 1093 for slice in range(p.slices/2,p.slices): # only do half of sphere (other half has no window) 1094 slice_start_frac = float(slice)/p.slices 1095 slice_stop_frac = float(slice+1)/p.slices 1096 phi_start = slice_start_frac * 2 * math.pi 1097 phi_stop = slice_stop_frac * 2 * math.pi 1098 x_start_upper = w_upper * math.cos(phi_start) 1099 x_start_lower = w_lower * math.cos(phi_start) 1100 x_stop_upper = w_upper * math.cos(phi_stop) 1101 x_stop_lower = w_lower * math.cos(phi_stop) 1102 z_start_upper = w_upper * math.sin(phi_start) 1103 z_start_lower = w_lower * math.sin(phi_start) 1104 z_stop_upper = w_upper * math.sin(phi_stop) 1105 z_stop_lower = w_lower * math.sin(phi_stop) 1106 1107 o = 0.5 1108 g = 0.5 / p.radius 1109 1110 if self._texture_s_is_azimuth: 1111 tex_s_start = slice_start_frac*2-1 1112 tex_s_stop = slice_stop_frac*2-1 1113 else: 1114 tex_s_start = x_start_upper*g+o 1115 tex_s_stop = x_stop_upper*g+o 1116 1117 gl.glTexCoord2f(tex_s_start,y_upper*g+o) 1118 gl.glVertex3f(x_start_upper, y_upper, z_start_upper) 1119 1120 gl.glTexCoord2f(tex_s_stop,y_upper*g+o) 1121 gl.glVertex3f(x_stop_upper, y_upper, z_stop_upper) 1122 1123 gl.glTexCoord2f(tex_s_stop,y_lower*g+o) 1124 gl.glVertex3f(x_stop_lower, y_lower, z_stop_lower) 1125 1126 gl.glTexCoord2f(tex_s_start,y_lower*g+o) 1127 gl.glVertex3f(x_start_lower, y_lower, z_start_lower) 1128 1129 gl.glEnd() 1130 gl.glEndList() 1131 1132 gl.glNewList(self.opaque_display_list_id,gl.GL_COMPILE) 1133 1134 gl.glBegin(gl.GL_QUADS) 1135 1136 for stack in range(p.stacks): 1137 stack_upper_frac = float(stack+1)/p.stacks 1138 stack_lower_frac = float(stack)/p.stacks 1139 theta_upper = stack_upper_frac * math.pi 1140 theta_lower = stack_lower_frac * math.pi 1141 y_upper = p.radius * math.cos( theta_upper ) 1142 w_upper = p.radius * math.sin( theta_upper ) 1143 y_lower = p.radius * math.cos( theta_lower ) 1144 w_lower = p.radius * math.sin( theta_lower ) 1145 for slice in range(p.slices/2): # half of sphere with no window 1146 slice_start_frac = float(slice)/p.slices 1147 slice_stop_frac = float(slice+1)/p.slices 1148 phi_start = slice_start_frac * 2 * math.pi 1149 phi_stop = slice_stop_frac * 2 * math.pi 1150 x_start_upper = w_upper * math.cos(phi_start) 1151 x_start_lower = w_lower * math.cos(phi_start) 1152 x_stop_upper = w_upper * math.cos(phi_stop) 1153 x_stop_lower = w_lower * math.cos(phi_stop) 1154 z_start_upper = w_upper * math.sin(phi_start) 1155 z_start_lower = w_lower * math.sin(phi_start) 1156 z_stop_upper = w_upper * math.sin(phi_stop) 1157 z_stop_lower = w_lower * math.sin(phi_stop) 1158 1159 gl.glVertex3f(x_start_upper, y_upper, z_start_upper) 1160 1161 gl.glVertex3f(x_stop_upper, y_upper, z_stop_upper) 1162 1163 gl.glVertex3f(x_stop_lower, y_lower, z_stop_lower) 1164 1165 gl.glVertex3f(x_start_lower, y_lower, z_start_lower) 1166 1167 gl.glEnd() 1168 gl.glEndList() 1169 self._cached_radius = p.radius 1170 self._cached_slices = p.slices 1171 self._cached_stacks = p.stacks 1172 gl.glPopMatrix()
1173
1174 - def draw(self):
1175 """Redraw the scene on every frame. 1176 """ 1177 p = self.parameters 1178 1179 if self._cached_radius != p.radius or self._cached_slices != p.slices or self._cached_stacks != p.stacks: 1180 self.__rebuild_display_lists() 1181 1182 if self._cached_window_shape != p.window_shape or self._cached_shape_radius_parameter != p.window_shape_radius_parameter: 1183 self.__rebuild_texture_object() 1184 1185 if p.window_shape == 'lat-long rectangle' and self._cached_shape_parameter2 != p.window_shape_parameter2: 1186 self.__rebuild_texture_object() 1187 1188 if self._cached_num_s_samples != p.num_s_samples or self._cached_num_t_samples != p.num_t_samples: 1189 self.__rebuild_texture_object() 1190 1191 if p.on: 1192 #gl.glPolygonMode( gl.GL_FRONT_AND_BACK, gl.GL_LINE ) 1193 if p.bit_depth != self.cached_bit_depth: 1194 self.calculate_bit_depth_dependencies() 1195 self.gl_internal_format = gl.GL_ALPHA # change from luminance to alpha 1196 self.format = gl.GL_ALPHA 1197 # Set OpenGL state variables 1198 gl.glEnable( gl.GL_DEPTH_TEST ) 1199 gl.glEnable( gl.GL_TEXTURE_2D ) 1200 gl.glEnable( gl.GL_BLEND ) 1201 1202 gl.glBlendFunc( gl.GL_ONE_MINUS_SRC_ALPHA, gl.GL_SRC_ALPHA ) # alpha 1.0 = transparent 1203 1204 gl.glBindTexture(gl.GL_TEXTURE_2D,self.texture_object_id) 1205 gl.glColor4f( *p.opaque_color ) 1206 gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_REPLACE) 1207 1208 # clear modelview matrix 1209 gl.glMatrixMode(gl.GL_MODELVIEW) 1210 gl.glPushMatrix() 1211 1212 # do the window position 1213 gl.glRotatef(p.window_center_azimuth,0.0,-1.0,0.0) 1214 gl.glRotatef(p.window_center_elevation,1.0,0.0,0.0) 1215 1216 gl.glCallList(self.windowed_display_list_id) 1217 gl.glCallList(self.opaque_display_list_id) 1218 gl.glPopMatrix()
1219