1
2
3
4
5
6
7
8
9
10
11 """
12 Texture (images mapped onto polygons) stimuli.
13
14 """
15
16
17
18
19
20
21
22 import logging
23
24 import VisionEgg
25 import VisionEgg.Core
26 import VisionEgg.ParameterTypes as ve_types
27
28 import Image, ImageDraw
29 import pygame.surface, pygame.image
30 import math, types, os
31 import numpy
32 import numpy.oldnumeric as numpyNumeric, numpy.oldnumeric.mlab as MLab
33
34 import VisionEgg.GL as gl
35 import OpenGL.GLU as glu
36
37
38
39
40
41 import _imaging
42 import ImageFile, ImageFileIO, BmpImagePlugin, JpegImagePlugin, PngImagePlugin
43
44 if Image.VERSION >= '1.1.3':
45 shrink_filter = Image.ANTIALIAS
46 else:
47 shrink_filter = Image.BICUBIC
48
49 array_types = [numpy.ndarray]
50
51 try:
52 import numarray
53 array_types.append( numarray.numarraycore.NumArray )
54 except ImportError:
55 pass
56 try:
57 import Numeric as orig_Numeric
58 array_types.append( orig_Numeric.ArrayType )
59 except ImportError:
60 pass
61
63 if type(A) in array_types:
64
65 return numpy.asarray(A)
66 else:
67 return A
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
97 return int(math.pow(2.0,math.ceil(math.log(f)/math.log(2.0))))
98
101
102 -class Texture(object):
103 """A 2 dimensional texture.
104
105 The pixel data can come from an image file, an image file stream,
106 an instance of Image from the Python Imaging Library, a numpy
107 array, or None.
108
109 If the data is a numpy array, floating point numbers are assumed
110 to be in the range 0.0 to 1.0, and integers are assumed to be in
111 the range 0 to 255. The first index is the row (y position), the
112 second index is the column (x position), and if it's RGB or RGBA
113 data, the third index specifies the color band. Thus, if the
114 texel data was 640 pixels wide by 480 pixels tall, the array would
115 have shape (480,640) for luminance information, (480,640,3) for
116 RGB information, and (480,640,4) for RGBA information.
117
118 The 2D texture data is not sent to OpenGL (video texture memory)
119 until the load() method is called. The unload() method may be
120 used to remove the data from OpenGL.
121
122 A reference to the original image data is maintained."""
123
124 __slots__ = ('texels',
125 'texture_object',
126 'size',
127 '_filename',
128 '_file_stream',
129 'buf_lf',
130 'buf_rf',
131 'buf_bf',
132 'buf_tf',
133 '_buf_l',
134 '_buf_r',
135 '_buf_b',
136 '_buf_t',
137 )
138
139 - def __init__(self,texels=None,size=None):
140 """Creates instance of Texture object.
141
142 texels -- Texture data. If not specified, a blank white
143 texture is created.
144 size -- If a tuple, force size of texture data if possible,
145 raising an exception if not. If None, has no effect.
146 """
147
148 if texels is None:
149 if size is None:
150 size = (256,256)
151 texels = Image.new("RGB",size,(255,255,255))
152
153 if type(texels) == types.FileType:
154 texels = Image.open(texels)
155 elif type(texels) in (types.StringType,types.UnicodeType):
156
157 if os.path.isfile(texels):
158
159 self._filename = texels
160 self._file_stream = open(texels,"rb")
161 texels = Image.open(texels)
162
163 texels = convert_to_numpy_if_array(texels)
164
165 if isinstance(texels, Image.Image):
166 if texels.mode == 'P':
167 texels = texels.convert('RGBX')
168 self.size = texels.size
169 elif isinstance(texels, pygame.surface.Surface):
170 self.size = texels.get_size()
171 elif isinstance(texels,numpy.ndarray):
172 if len(texels.shape) == 3:
173 if texels.shape[2] not in [3,4]:
174 raise ValueError("Only luminance (rank 2), and RGB, RGBA (rank 3) arrays allowed")
175 elif len(texels.shape) != 2:
176 raise ValueError("Only luminance (rank 2), and RGB, RGBA (rank 3) arrays allowed")
177 self.size = ( texels.shape[1], texels.shape[0] )
178 else:
179 raise TypeError("texel data could not be recognized. (Use a PIL Image, numpy array, or pygame surface.)")
180
181 self.texels = texels
182 self.texture_object = None
183
184 if size is not None and size != self.size:
185 raise ValueError("size was specified, but data could not be rescaled")
186
188 """Update texture data
189
190 This method does nothing, but may be overriden in classes that
191 need to update their texture data whenever drawn.
192
193 It it called by the draw() method of any stimuli using
194 textures when its texture object is active, so it can safely
195 use put_sub_image() to manipulate its own texture data.
196 """
197 pass
198
199 - def make_half_size(self):
200 if self.texture_object is not None:
201 raise RuntimeError("make_half_size() only available BEFORE texture loaded to OpenGL.")
202
203 if isinstance(self.texels,Image.Image):
204 w = self.size[0]/2
205 h = self.size[1]/2
206 small_texels = self.texels.resize((w,h),shrink_filter)
207 self.texels = small_texels
208 self.size = (w,h)
209 else:
210 raise NotImplementedError("Texture too large, but rescaling only implemented for PIL images.")
211
213 """Unload texture data from video texture memory.
214
215 This only removes data from the video texture memory if there
216 are no other references to the TextureObject instance. To
217 ensure this, all references to the texture_object argument
218 passed to the load() method should be deleted."""
219
220 self.texture_object = None
221
223 """Return texel data as PIL image"""
224 if isinstance(self.texels,numpy.ndarray):
225 if len(self.texels.shape) == 2:
226 a = self.texels
227 if a.dtype == numpy.uint8:
228 mode = "L"
229 elif a.dtype == numpy.float32:
230 mode = "F"
231 else:
232 raise ValueError("unsupported image mode")
233 return Image.fromstring(mode, (a.shape[1], a.shape[0]), a.tostring())
234 else:
235 raise NotImplementedError("Currently only luminance data can be converted to images")
236 elif isinstance(self.texels, Image.Image):
237 return self.texels
238 else:
239 raise NotImplementedError("Don't know how to convert texel data to PIL image")
240
242 logger = logging.getLogger('VisionEgg.Textures')
243 logger.warning("Using deprecated method get_pixels_as_image(). "
244 "Use get_texels_as_image() instead.")
245 return self.get_texels_as_image()
246
247 - def load(self,
248 texture_object,
249 build_mipmaps = True,
250 rescale_original_to_fill_texture_object = False,
251 internal_format=gl.GL_RGB):
252 """Load texture data to video texture memory.
253
254 This will cause the texture data to become resident in OpenGL
255 video texture memory, enabling fast drawing.
256
257 The texture_object argument is used to specify an instance of
258 the TextureObject class, which is a wrapper for the OpenGL
259 texture object holding the resident texture.
260
261 To remove a texture from OpenGL's resident textures: TextureObject passed as the texture_object argument and 2)
262 call the unload() method"""
263
264 assert( isinstance( texture_object, TextureObject ))
265 assert( texture_object.dimensions == 2 )
266
267 width, height = self.size
268
269 width_pow2 = next_power_of_2(width)
270 height_pow2 = next_power_of_2(height)
271
272 if rescale_original_to_fill_texture_object:
273 if not isinstance(self.texels,Image.Image):
274 raise NotImplementedError("Automatic rescaling not implemented for this texel data type.")
275
276
277 self.buf_lf = 0.0
278 self.buf_rf = float(width)/width_pow2
279 self.buf_bf = 0.0
280 self.buf_tf = float(height)/height_pow2
281
282
283 self._buf_l = 0
284 self._buf_r = width
285 self._buf_b = 0
286 self._buf_t = height
287
288 if width != width_pow2 or height != height_pow2:
289 if isinstance(self.texels,numpy.ndarray):
290 if len(self.texels.shape) == 2:
291 buffer = numpy.zeros( (height_pow2,width_pow2), dtype=self.texels.dtype )
292 buffer[0:height,0:width] = self.texels
293 elif len(self.texels.shape) == 3:
294 buffer = numpy.zeros( (height_pow2,width_pow2,self.texels.shape[2]), dtype=self.texels.dtype )
295 buffer[0:height,0:width,:] = self.texels
296 else:
297 raise RuntimeError("Unexpected shape for self.texels")
298
299 elif isinstance(self.texels, Image.Image):
300 if rescale_original_to_fill_texture_object:
301
302 self.buf_lf = 0.0
303 self.buf_rf = 1.0
304 self.buf_bf = 0.0
305 self.buf_tf = 1.0
306
307 self._buf_l = 0
308 self._buf_r = width_pow2
309 self._buf_t = 0
310 self._buf_b = height_pow2
311
312 buffer = self.texels.resize((width_pow2,height_pow2),shrink_filter)
313
314 self.size = (width_pow2, height_pow2)
315 else:
316 buffer = Image.new(self.texels.mode,(width_pow2, height_pow2))
317 buffer.paste( self.texels, (0,height_pow2-height,width,height_pow2))
318 elif isinstance(self.texels, pygame.surface.Surface):
319 buffer = pygame.surface.Surface( (width_pow2, height_pow2),
320 self.texels.get_flags(),
321 self.texels.get_bitsize() )
322 buffer.blit( self.texels, (0,height_pow2-height) )
323 else:
324 raise RuntimeError("texel data not recognized - changed?")
325 else:
326 buffer = self.texels
327
328
329 if not build_mipmaps:
330 texture_object.put_new_image( buffer, internal_format=internal_format, mipmap_level=0 )
331 else:
332 if 0:
333
334 texture_object.put_new_image_build_mipmaps( buffer, internal_format=internal_format )
335 else:
336
337 texture_object.put_new_image( buffer, internal_format=internal_format, mipmap_level=0 )
338 if not isinstance(self.texels, Image.Image):
339 raise NotImplementedError(
340 "Building of mipmaps not implemented for this texel "+\
341 "data type. (Use PIL Images or set parameter "+\
342 "mipmaps_enabled = False.)")
343 this_width, this_height = self.size
344 biggest_dim = max(this_width,this_height)
345 mipmap_level = 1
346 while biggest_dim > 1:
347 this_width = this_width/2.0
348 this_height = this_height/2.0
349
350 width_pix = int(math.ceil(this_width))
351 height_pix = int(math.ceil(this_height))
352 shrunk = self.texels.resize((width_pix,height_pix),shrink_filter)
353
354 width_pow2 = next_power_of_2(width_pix)
355 height_pow2 = next_power_of_2(height_pix)
356
357 im = Image.new(shrunk.mode,(width_pow2,height_pow2))
358 im.paste(shrunk,(0,height_pow2-height_pix,width_pix,height_pow2))
359
360 texture_object.put_new_image( im,
361 mipmap_level=mipmap_level,
362 internal_format = internal_format,
363 check_opengl_errors = False,
364 )
365
366 mipmap_level += 1
367 biggest_dim = max(this_width,this_height)
368
369
370 self.texture_object = texture_object
371
373 return self.texture_object
374
375 -class TextureFromFile( Texture ):
376 """DEPRECATED."""
377 - def __init__(self, filename ):
378 logger = logging.getLogger('VisionEgg.Textures')
379 logger.warning("class TextureFromFile deprecated, use class "
380 "Texture instead.")
381 Texture.__init__(self, filename)
382
383 -class TextureObject(object):
384 """Texture data in OpenGL. Potentially resident in video texture memory.
385
386 This class encapsulates the state variables in OpenGL texture objects. Do not
387 change attribute values directly. Use the methods provided instead."""
388
389 __slots__ = (
390 'min_filter',
391 'mag_filter',
392 'wrap_mode_r',
393 'wrap_mode_s',
394 'wrap_mode_t',
395 'border_color',
396 'target',
397 'dimensions',
398 'gl_id',
399 '__gl_module__',
400 )
401
402 _cube_map_side_names = ['positive_x', 'negative_x',
403 'positive_y', 'negative_y',
404 'positive_z', 'negative_z']
405
406 - def __init__(self,
407 dimensions = 2):
408 if dimensions not in [1,2,3,'cube']:
409 raise ValueError("TextureObject dimensions must be 1,2,3, or 'cube'")
410
411 self.min_filter = gl.GL_NEAREST_MIPMAP_LINEAR
412 self.mag_filter = gl.GL_LINEAR
413 self.wrap_mode_s = gl.GL_REPEAT
414 if dimensions != 1:
415 self.wrap_mode_t = gl.GL_REPEAT
416 if dimensions == 3:
417 self.wrap_mode_r = gl.GL_REPEAT
418 self.border_color = (0, 0, 0, 0)
419
420 if dimensions == 1:
421 self.target = gl.GL_TEXTURE_1D
422 elif dimensions == 2:
423 self.target = gl.GL_TEXTURE_2D
424 elif dimensions == 3:
425 self.target = gl.GL_TEXTURE_3D
426 elif dimensions == 'cube':
427 self.target = gl.GL_TEXTURE_CUBE
428
429 self.dimensions = dimensions
430 self.gl_id = gl.glGenTextures(1)
431 self.__gl_module__ = gl
432
434 self.__gl_module__.glDeleteTextures(self.gl_id)
435
436 - def is_resident(self):
437 return gl.glAreTexturesResident( self.gl_id )
438
439 - def set_priority(self, priority):
440 gl.glPrioritizeTextures( [self.gl_id], [priority] )
441
442 - def set_min_filter(self, filter):
443 gl.glBindTexture(self.target, self.gl_id)
444 gl.glTexParameteri( self.target, gl.GL_TEXTURE_MIN_FILTER,filter)
445 self.min_filter = filter
446
447 - def set_mag_filter(self, filter):
448 gl.glBindTexture( self.target, self.gl_id)
449 gl.glTexParameteri( self.target, gl.GL_TEXTURE_MAG_FILTER, filter)
450 self.mag_filter = filter
451
452 - def set_wrap_mode_s(self, wrap_mode):
453 """Set to GL_CLAMP, GL_CLAMP_TO_EDGE, GL_REPEAT, or GL_CLAMP_TO_BORDER"""
454 gl.glBindTexture( self.target, self.gl_id)
455 gl.glTexParameteri( self.target, gl.GL_TEXTURE_WRAP_S, wrap_mode)
456 self.wrap_mode_s = wrap_mode
457
458 - def set_wrap_mode_t(self, wrap_mode):
459 """Set to GL_CLAMP, GL_CLAMP_TO_EDGE, GL_REPEAT, or GL_CLAMP_TO_BORDER"""
460 gl.glBindTexture( self.target, self.gl_id)
461 gl.glTexParameteri( self.target, gl.GL_TEXTURE_WRAP_T, wrap_mode)
462 self.wrap_mode_t = wrap_mode
463
464 - def set_wrap_mode_r(self, wrap_mode):
465 """Set to GL_CLAMP, GL_CLAMP_TO_EDGE, GL_REPEAT, or GL_CLAMP_TO_BORDER"""
466 gl.glBindTexture( self.target, self.gl_id)
467 gl.glTexParameteri( self.target, gl.GL_TEXTURE_WRAP_R, wrap_mode)
468 self.wrap_mode_r = wrap_mode
469
470 - def set_border_color(self, border_color):
471 """Set to a sequence of 4 floats in the range 0.0 to 1.0"""
472 gl.glBindTexture( self.target, self.gl_id)
473 gl.glTexParameteriv( self.target, gl.GL_TEXTURE_BORDER_COLOR, border_color)
474 self.border_color = border_color
475
476 - def put_new_image(self,
477 texel_data,
478 mipmap_level = 0,
479 border = 0,
480 check_opengl_errors = True,
481 internal_format = gl.GL_RGB,
482 data_format = None,
483 data_type = None,
484 cube_side = None,
485 ):
486
487 """Put numpy array or PIL Image into OpenGL as texture data.
488
489 The texel_data parameter contains the texture data. If it is
490 a numpy array, it must be 1D, 2D, or 3D data in grayscale or
491 color (RGB or RGBA). Remember that OpenGL begins its textures
492 from the lower left corner, so texel_data[0,:] = 1.0 would set
493 the bottom line of the texture to white, while texel_data[:,0]
494 = 1.0 would set the left line of the texture to white.
495
496 The mipmap_level parameter specifies which of the texture
497 object's mipmap arrays you are filling.
498
499 The border parameter specifies the width of the border.
500
501 The check_opengl_errors parameter turns on (more
502 comprehensible) error messages when something goes wrong. It
503 also slows down performance a little bit.
504
505 The internal_format parameter specifies the format in which
506 the image data is stored on the video card. See the OpenGL
507 specification for all possible values.
508
509 If the data_format parameter is None (the default), an attempt
510 is made to guess data_format according to the following
511 description. For numpy arrays: If texel_data.shape is equal
512 to the dimensions of the texture object, texel_data is assumed
513 to contain luminance (grayscale) information and data_format
514 is set to GL_LUMINANCE. If texel_data.shape is equal to one
515 plus the dimensions of the texture object, texel_data is
516 assumed to contain color information. If texel_data.shape[-1]
517 is 3, this is assumed to be RGB data and data_format is set to
518 GL_RGB. If, texel_data.shape[-1] is 4, this is assumed to be
519 RGBA data and data_format is set to GL_RGBA. For PIL images:
520 the "mode" attribute is queried.
521
522 If the data_type parameter is None (the default), it is set to
523 GL_UNSIGNED_BYTE. For numpy arrays: texel_data is (re)cast
524 to UInt8 and, if it is a floating point type, values are
525 assumed to be in the range 0.0-1.0 and are scaled to the range
526 0-255. If the data_type parameter is not None, the texel_data
527 is not rescaled or recast. Currently only GL_UNSIGNED_BYTE is
528 supported. For PIL images: texel_data is used as unsigned
529 bytes. This is the usual format for common computer graphics
530 files."""
531
532 texel_data = convert_to_numpy_if_array(texel_data)
533 if isinstance(texel_data,numpy.ndarray):
534 if self.dimensions != 'cube':
535 assert(cube_side == None)
536 data_dimensions = len(texel_data.shape)
537 assert((data_dimensions == self.dimensions) or (data_dimensions == self.dimensions+1))
538 else:
539 assert(cube_side in TextureObject._cube_map_side_names)
540 elif isinstance(texel_data,Image.Image):
541 assert( self.dimensions == 2 )
542 elif isinstance(texel_data,pygame.surface.Surface):
543 assert( self.dimensions == 2 )
544 else:
545 raise TypeError("Expecting numpy array, PIL image, or pygame surface")
546
547
548 gl.glBindTexture(self.target, self.gl_id)
549
550
551 if data_format is None:
552 if isinstance(texel_data,numpy.ndarray):
553 if len(texel_data.shape) == self.dimensions:
554 data_format = gl.GL_LUMINANCE
555 elif len(texel_data.shape) == (self.dimensions+1):
556 if texel_data.shape[-1] == 3:
557 data_format = gl.GL_RGB
558 elif texel_data.shape[-1] == 4:
559 data_format = gl.GL_RGBA
560 else:
561 raise RuntimeError("Couldn't determine a format for your texel_data.")
562 else:
563 raise RuntimeError("Couldn't determine a format for your texel_data.")
564 elif isinstance(texel_data,Image.Image):
565 if texel_data.mode == 'L':
566 data_format = gl.GL_LUMINANCE
567 elif texel_data.mode == 'RGB':
568 data_format = gl.GL_RGB
569 elif texel_data.mode in ('RGBA','RGBX'):
570 data_format = gl.GL_RGBA
571 elif texel_data.mode == 'P':
572 raise NotImplementedError("Paletted images are not supported.")
573 else:
574 raise RuntimeError("Couldn't determine format for your texel_data. (PIL mode = '%s')"%texel_data.mode)
575 elif isinstance(texel_data,pygame.surface.Surface):
576 if texel_data.get_alpha():
577 data_format = gl.GL_RGBA
578 else:
579 data_format = gl.GL_RGB
580
581 if data_type is None:
582 data_type = gl.GL_UNSIGNED_BYTE
583 if isinstance(texel_data,numpy.ndarray):
584 if texel_data.dtype == numpy.float:
585 texel_data = texel_data*255.0
586
587 if data_type == gl.GL_UNSIGNED_BYTE:
588 if isinstance(texel_data,numpy.ndarray):
589 texel_data = texel_data.astype(numpy.uint8)
590 else:
591 raise NotImplementedError("Only data_type GL_UNSIGNED_BYTE currently supported")
592
593
594 if self.dimensions == 1:
595
596 width = texel_data.shape[0]
597 if not is_power_of_2(width): raise ValueError("texel_data does not have all dimensions == n^2")
598 else:
599 if isinstance(texel_data,numpy.ndarray):
600 width = texel_data.shape[1]
601 height = texel_data.shape[0]
602 elif isinstance(texel_data,Image.Image):
603 width, height = texel_data.size
604 elif isinstance(texel_data,pygame.surface.Surface):
605 width, height = texel_data.get_size()
606 if not is_power_of_2(width): raise ValueError("texel_data does not have all dimensions == n^2")
607 if not is_power_of_2(height): raise ValueError("texel_data does not have all dimensions == n^2")
608 if self.dimensions == 3:
609
610 depth = texel_data.shape[2]
611 if not is_power_of_2(depth): raise ValueError("texel_data does not have all dimensions == n^2")
612
613 if self.dimensions in [2,'cube']:
614 if isinstance(texel_data,numpy.ndarray):
615 raw_data = texel_data.tostring()
616 elif isinstance(texel_data,Image.Image):
617 raw_data = texel_data.tostring('raw',texel_data.mode,0,-1)
618 elif isinstance(texel_data,pygame.surface.Surface):
619 if texel_data.get_alpha():
620 raw_data = pygame.image.tostring(texel_data,'RGBA',1)
621 else:
622 raw_data = pygame.image.tostring(texel_data,'RGB',1)
623
624
625 if check_opengl_errors:
626 max_dim = gl.glGetIntegerv( gl.GL_MAX_TEXTURE_SIZE )
627 if width > max_dim:
628 raise TextureTooLargeError("texel_data is too wide for your video system.")
629 if self.dimensions == 1:
630 gl.glTexImage1Dub(gl.GL_PROXY_TEXTURE_1D,
631 mipmap_level,
632 internal_format,
633 border,
634 data_format,
635 texel_data)
636 if gl.glGetTexLevelParameteriv(gl.GL_PROXY_TEXTURE_1D,mipmap_level,gl.GL_TEXTURE_WIDTH) == 0:
637 raise TextureTooLargeError("texel_data is too wide for your video system.")
638 elif self.dimensions in [2,'cube']:
639 if height > max_dim:
640 raise TextureTooLargeError("texel_data is too tall for your video system.")
641 if self.dimensions == 2:
642 target = gl.GL_PROXY_TEXTURE_2D
643 else:
644 target = gl.GL_PROXY_CUBE_MAP
645 gl.glTexImage2D(target,
646 mipmap_level,
647 internal_format,
648 width,
649 height,
650 border,
651 data_format,
652 data_type,
653 raw_data)
654 if gl.glGetTexLevelParameteriv(target,
655 mipmap_level,
656 gl.GL_TEXTURE_WIDTH) == 0:
657 raise TextureTooLargeError("texel_data is too wide for your video system.")
658 if gl.glGetTexLevelParameteriv(target,mipmap_level,gl.GL_TEXTURE_HEIGHT) == 0:
659 raise TextureTooLargeError("texel_data is too tall for your video system.")
660 elif self.dimensions == 3:
661 if max(height,depth) > max_dim:
662 raise TextureTooLargeError("texel_data is too large for your video system.")
663 gl.glTexImage3Dub(gl.GL_PROXY_TEXTURE_3D,
664 mipmap_level,
665 internal_format,
666 border,
667 data_format,
668 texel_data)
669 if gl.glGetTexLevelParameteriv(gl.GL_PROXY_TEXTURE_3D,mipmap_level,gl.GL_TEXTURE_WIDTH) == 0:
670 raise TextureTooLargeError("texel_data is too wide for your video system.")
671 if gl.glGetTexLevelParameteriv(gl.GL_PROXY_TEXTURE_3D,mipmap_level,gl.GL_TEXTURE_HEIGHT) == 0:
672 raise TextureTooLargeError("texel_data is too tall for your video system.")
673 if gl.glGetTexLevelParameteriv(gl.GL_PROXY_TEXTURE_3D,mipmap_level,gl.GL_TEXTURE_DEPTH) == 0:
674 raise TextureTooLargeError("texel_data is too deep for your video system.")
675 else:
676 raise RuntimeError("Unknown number of dimensions.")
677
678
679 if self.dimensions == 1:
680 gl.glTexImage1Dub(gl.GL_TEXTURE_1D,
681 mipmap_level,
682 internal_format,
683 border,
684 data_format,
685 texel_data)
686 elif self.dimensions in [2,'cube']:
687 if self.dimensions == 2:
688 target = gl.GL_TEXTURE_2D
689 else:
690 target_name = 'GL_CUBE_MAP_'+cube_side.upper()
691 target = getattr(gl,target_name)
692 gl.glTexImage2D(target,
693 mipmap_level,
694 internal_format,
695 width,
696 height,
697 border,
698 data_format,
699 data_type,
700 raw_data)
701 elif self.dimensions == 3:
702 gl.glTexImage3Dub(gl.GL_TEXTURE_3D,
703 mipmap_level,
704 internal_format,
705 border,
706 data_format,
707 texel_data)
708 else:
709 raise RuntimeError("Unknown number of dimensions.")
710
711 - def put_new_image_build_mipmaps(self,
712 texel_data,
713 internal_format = gl.GL_RGB,
714 data_format = None,
715 data_type = None,
716 cube_side = None,
717 ):
718
719 """Similar to put_new_image(), but builds mipmaps."""
720
721 if self.dimensions != 2:
722 raise ValueError("can only handle 2D texel data for automatic mipmap building")
723 texel_data = convert_to_numpy_if_array(texel_data)
724 if isinstance(texel_data,numpy.ndarray):
725 assert(cube_side == None)
726 data_dimensions = len(texel_data.shape)
727 assert((data_dimensions == self.dimensions) or (data_dimensions == self.dimensions+1))
728 elif isinstance(texel_data,Image.Image):
729 assert( self.dimensions == 2 )
730 elif isinstance(texel_data,pygame.surface.Surface):
731 assert( self.dimensions == 2 )
732 else:
733 raise TypeError("Expecting numpy array, PIL image, or pygame surface")
734
735
736 gl.glBindTexture(self.target, self.gl_id)
737
738
739 if data_format is None:
740 if isinstance(texel_data,numpy.ndarray):
741 if len(texel_data.shape) == self.dimensions:
742 data_format = gl.GL_LUMINANCE
743 elif len(texel_data.shape) == (self.dimensions+1):
744 if texel_data.shape[-1] == 3:
745 data_format = gl.GL_RGB
746 elif texel_data.shape[-1] == 4:
747 data_format = gl.GL_RGBA
748 else:
749 raise RuntimeError("Couldn't determine a format for your texel_data.")
750 else:
751 raise RuntimeError("Couldn't determine a format for your texel_data.")
752 elif isinstance(texel_data,Image.Image):
753 if texel_data.mode == 'L':
754 data_format = gl.GL_LUMINANCE
755 elif texel_data.mode == 'RGB':
756 data_format = gl.GL_RGB
757 elif texel_data.mode in ['RGBA','RGBX']:
758 data_format = gl.GL_RGBA
759 elif texel_data.mode == 'P':
760 raise NotImplementedError("Paletted images are not supported.")
761 else:
762 raise RuntimeError("Couldn't determine format for your texel_data. (PIL mode = '%s')"%texel_data.mode)
763 elif isinstance(texel_data,pygame.surface.Surface):
764 if texel_data.get_alpha():
765 data_format = gl.GL_RGBA
766 else:
767 data_format = gl.GL_RGB
768
769 if data_type is None:
770 data_type = gl.GL_UNSIGNED_BYTE
771 if isinstance(texel_data,numpy.ndarray):
772 if texel_data.dtype == numpy.float:
773 texel_data = texel_data*255.0
774
775 if data_type == gl.GL_UNSIGNED_BYTE:
776 if isinstance(texel_data,numpy.ndarray):
777 texel_data = texel_data.astype(numpy.uint8)
778 else:
779 raise NotImplementedError("Only data_type GL_UNSIGNED_BYTE currently supported")
780
781 if isinstance(texel_data,numpy.ndarray):
782 width = texel_data.shape[1]
783 height = texel_data.shape[0]
784 elif isinstance(texel_data,Image.Image):
785 width, height = texel_data.size
786 elif isinstance(texel_data,pygame.surface.Surface):
787 width, height = texel_data.get_size()
788 if not is_power_of_2(width): raise ValueError("texel_data does not have all dimensions == n^2")
789 if not is_power_of_2(height): raise ValueError("texel_data does not have all dimensions == n^2")
790
791 if isinstance(texel_data,numpy.ndarray):
792 raw_data = texel_data.tostring()
793 elif isinstance(texel_data,Image.Image):
794 raw_data = texel_data.tostring('raw',texel_data.mode,0,-1)
795 elif isinstance(texel_data,pygame.surface.Surface):
796 if texel_data.get_alpha():
797 raw_data = pygame.image.tostring(texel_data,'RGBA',1)
798 else:
799 raw_data = pygame.image.tostring(texel_data,'RGB',1)
800
801 args = (self.target,
802 internal_format,
803 width,
804 height,
805 data_format,
806 data_type)
807
808 print 'args',args
809
810 glu.gluBuild2DMipmaps(self.target,
811 internal_format,
812 width,
813 height,
814 data_format,
815 data_type,
816 raw_data)
817
818 - def put_sub_image(self,
819 texel_data,
820 mipmap_level = 0,
821 offset_tuple = None,
822 data_format = None,
823 data_type = None,
824 cube_side = None,
825 ):
826
827 """Replace all or part of a texture object.
828
829 This is faster that put_new_image(), and can be used to
830 rapidly update textures.
831
832 The offset_tuple parameter determines the lower left corner
833 (for 2D textures) of your data in pixel units. For example,
834 (0,0) would be no offset and thus the new data would be placed
835 in the lower left of the texture.
836
837 For an explanation of most parameters, see the
838 put_new_image() method."""
839
840 texel_data = convert_to_numpy_if_array(texel_data)
841 if isinstance(texel_data,numpy.ndarray):
842 if self.dimensions != 'cube':
843 assert(cube_side == None)
844 data_dimensions = len(texel_data.shape)
845 assert((data_dimensions == self.dimensions) or (data_dimensions == self.dimensions+1))
846 else:
847 assert(cube_side in TextureObject._cube_map_side_names)
848 elif isinstance(texel_data,Image.Image):
849 assert( self.dimensions == 2 )
850 elif isinstance(texel_data,pygame.surface.Surface):
851 assert( self.dimensions == 2 )
852 else:
853 raise TypeError("Expecting numpy array, PIL image, or pygame surface")
854
855
856 gl.glBindTexture(self.target, self.gl_id)
857
858
859 data = texel_data
860
861 if data_format is None:
862 if isinstance(texel_data,numpy.ndarray):
863 if len(data.shape) == self.dimensions:
864 data_format = gl.GL_LUMINANCE
865 elif len(data.shape) == (self.dimensions+1):
866 if data.shape[-1] == 3:
867 data_format = gl.GL_RGB
868 elif data.shape[-1] == 4:
869 data_format = gl.GL_RGBA
870 else:
871 raise RuntimeError("Couldn't determine a format for your texel_data.")
872 else:
873 raise RuntimeError("Couldn't determine a format for your texel_data.")
874 elif isinstance(texel_data,Image.Image):
875 if data.mode == 'L':
876 data_format = gl.GL_LUMINANCE
877 elif data.mode == 'RGB':
878 data_format = gl.GL_RGB
879 elif data.mode in ['RGBA','RGBX']:
880 data_format = gl.GL_RGBA
881 elif data.mode == 'P':
882 raise NotImplementedError("Paletted images are not supported.")
883 else:
884 raise RuntimeError("Couldn't determine format for your texel_data. (PIL mode = '%s')"%data.mode)
885 elif isinstance(texel_data,pygame.surface.Surface):
886 if data.get_alpha():
887 data_format = gl.GL_RGBA
888 else:
889 data_format = gl.GL_RGB
890
891 if data_type is None:
892 data_type = gl.GL_UNSIGNED_BYTE
893 if isinstance(data,numpy.ndarray):
894 if data.dtype == numpy.float:
895 data = data*255.0
896
897 if data_type == gl.GL_UNSIGNED_BYTE:
898 if isinstance(data,numpy.ndarray):
899 data = data.astype(numpy.uint8)
900 else:
901 raise NotImplementedError("Only data_type GL_UNSIGNED_BYTE currently supported")
902
903 if self.dimensions == 1:
904 if offset_tuple is None:
905 x_offset = 0
906 else:
907 x_offset = offset_tuple[0]
908 width = data.shape[0]
909 raw_data = data.astype(numpy.uint8).tostring()
910 gl.glTexSubImage1D(gl.GL_TEXTURE_1D,
911 mipmap_level,
912 x_offset,
913 width,
914 data_format,
915 data_type,
916 raw_data)
917 elif self.dimensions in [2,'cube']:
918 if self.dimensions == 2:
919 target = gl.GL_TEXTURE_2D
920 else:
921 target_name = 'GL_CUBE_MAP_'+cube_side.upper()
922 target = getattr(gl,target_name)
923 if offset_tuple is None:
924 x_offset = y_offset = 0
925 else:
926 x_offset, y_offset = offset_tuple
927 if isinstance(data,numpy.ndarray):
928 width = data.shape[1]
929 height = data.shape[0]
930 raw_data = data.astype(numpy.uint8).tostring()
931 elif isinstance(texel_data,Image.Image):
932 width = data.size[0]
933 height = data.size[1]
934 raw_data = data.tostring('raw',data.mode,0,-1)
935 elif isinstance(texel_data,pygame.surface.Surface):
936 width, height = texel_data.get_size()
937 if data.get_alpha():
938 raw_data = pygame.image.tostring(texel_data,'RGBA',1)
939 else:
940 raw_data = pygame.image.tostring(texel_data,'RGB',1)
941 gl.glTexSubImage2D(target,
942 mipmap_level,
943 x_offset,
944 y_offset,
945 width,
946 height,
947 data_format,
948 data_type,
949 raw_data)
950 elif self.dimensions == 3:
951 raise RuntimeError("Cannot put_sub_image on 3D texture_object.")
952 else:
953 raise RuntimeError("Unknown number of dimensions.")
954
955 - def put_new_framebuffer(self,
956 buffer='back',
957 mipmap_level = 0,
958 border = 0,
959 framebuffer_lowerleft = None,
960 size = None,
961 internal_format = gl.GL_RGB,
962 cube_side = None,
963 ):
964
965 """Replace texture object with the framebuffer contents.
966
967 The framebuffer_lowerleft parameter determines the lower left
968 corner of the framebuffer region from which to copy texel data
969 in pixel units. For example, (0,0) would be no offset and
970 thus the new data would be placed from the lower left of the
971 framebuffer.
972
973 For an explanation of most parameters, see the
974 put_new_image() method."""
975
976 if self.dimensions != 2:
977 raise RuntimeError("put_new_framebuffer only supported for 2D textures.")
978
979 if buffer == 'front':
980 gl.glReadBuffer( gl.GL_FRONT )
981 elif buffer == 'back':
982 gl.glReadBuffer( gl.GL_BACK )
983 else:
984 raise ValueError('No support for "%s" framebuffer'%buffer)
985
986
987 gl.glBindTexture(self.target, self.gl_id)
988
989 if framebuffer_lowerleft is None:
990 framebuffer_lowerleft = (0,0)
991 x,y = framebuffer_lowerleft
992
993 if size is None:
994 raise ValueError("Must specify size for put_new_framebuffer(): cannot guess")
995
996
997 width, height = size
998 if not is_power_of_2(width): raise ValueError("texel_data does not have all dimensions == n^2")
999 if not is_power_of_2(height): raise ValueError("texel_data does not have all dimensions == n^2")
1000
1001 target = gl.GL_TEXTURE_2D
1002 gl.glCopyTexImage2D(target,
1003 mipmap_level,
1004 internal_format,
1005 x,
1006 y,
1007 width,
1008 height,
1009 border)
1010
1011
1012
1013
1014
1015
1016
1017 -class TextureStimulusBaseClass(VisionEgg.Core.Stimulus):
1018 """Parameters common to all stimuli that use textures.
1019
1020 Don't instantiate this class directly.
1021
1022 Parameters
1023 ==========
1024 texture -- source of texture data (Instance of <class 'VisionEgg.Textures.Texture'>)
1025 Default: (determined at runtime)
1026 texture_mag_filter -- OpenGL filter enum (Integer)
1027 Default: GL_LINEAR (9729)
1028 texture_min_filter -- OpenGL filter enum (Integer)
1029 Default: (GL enum determined at runtime)
1030 texture_wrap_s -- OpenGL texture wrap enum (Integer)
1031 Default: (GL enum determined at runtime)
1032 texture_wrap_t -- OpenGL texture wrap enum (Integer)
1033 Default: (GL enum determined at runtime)
1034
1035 Constant Parameters
1036 ===================
1037 internal_format -- format with which OpenGL uses texture data (OpenGL data type enum) (Integer)
1038 Default: GL_RGB (6407)
1039 mipmaps_enabled -- Are mipmaps enabled? (Boolean)
1040 Default: True
1041 shrink_texture_ok -- Allow automatic shrinking of texture if too big? (Boolean)
1042 Default: False
1043 """
1044
1045 parameters_and_defaults = {
1046 'texture':(None,
1047 ve_types.Instance(Texture),
1048 "source of texture data"),
1049 'texture_mag_filter':(gl.GL_LINEAR,
1050 ve_types.Integer,
1051 "OpenGL filter enum",
1052 VisionEgg.ParameterDefinition.OPENGL_ENUM),
1053 'texture_min_filter':(None,
1054 ve_types.Integer,
1055 "OpenGL filter enum",
1056 VisionEgg.ParameterDefinition.OPENGL_ENUM),
1057 'texture_wrap_s':(None,
1058 ve_types.Integer,
1059 "OpenGL texture wrap enum",
1060 VisionEgg.ParameterDefinition.OPENGL_ENUM),
1061 'texture_wrap_t':(None,
1062 ve_types.Integer,
1063 "OpenGL texture wrap enum",
1064 VisionEgg.ParameterDefinition.OPENGL_ENUM),
1065 }
1066
1067 constant_parameters_and_defaults = {
1068 'internal_format':(gl.GL_RGB,
1069 ve_types.Integer,
1070 "format with which OpenGL uses texture data (OpenGL data type enum)",
1071 VisionEgg.ParameterDefinition.OPENGL_ENUM),
1072 'mipmaps_enabled':(True,
1073 ve_types.Boolean,
1074 "Are mipmaps enabled?"),
1075 'shrink_texture_ok':(False,
1076 ve_types.Boolean,
1077 "Allow automatic shrinking of texture if too big?"),
1078 }
1079
1080 __slots__ = (
1081 'texture_object',
1082 '_using_texture',
1083 )
1084
1085 _mipmap_modes = [gl.GL_LINEAR_MIPMAP_LINEAR,gl.GL_LINEAR_MIPMAP_NEAREST,
1086 gl.GL_NEAREST_MIPMAP_LINEAR,gl.GL_NEAREST_MIPMAP_NEAREST]
1087
1088 - def __init__(self,**kw):
1089 VisionEgg.Core.Stimulus.__init__(self,**kw)
1090
1091 if self.parameters.texture is None:
1092
1093 self.parameters.texture = Texture()
1094
1095 if self.parameters.texture_min_filter is None:
1096
1097 if self.constant_parameters.mipmaps_enabled:
1098 self.parameters.texture_min_filter = gl.GL_LINEAR_MIPMAP_LINEAR
1099 else:
1100 self.parameters.texture_min_filter = gl.GL_LINEAR
1101
1102 if not self.constant_parameters.mipmaps_enabled:
1103 if self.parameters.texture_min_filter in TextureStimulusBaseClass._mipmap_modes:
1104 raise ValueError("texture_min_filter cannot be a mipmap type if mipmaps not enabled.")
1105
1106
1107
1108
1109 if self.parameters.texture_wrap_s is None:
1110 self.parameters.texture_wrap_s = gl.GL_CLAMP_TO_EDGE
1111 if self.parameters.texture_wrap_t is None:
1112 self.parameters.texture_wrap_t = gl.GL_CLAMP_TO_EDGE
1113
1114
1115 self.texture_object = TextureObject(dimensions=2)
1116
1117 self._reload_texture()
1118
1119 - def _reload_texture(self):
1120 """(Re)load texture to OpenGL"""
1121 p = self.parameters
1122 self._using_texture = p.texture
1123
1124 if not self.constant_parameters.shrink_texture_ok:
1125
1126 p.texture.load( self.texture_object,
1127 internal_format = self.constant_parameters.internal_format,
1128 build_mipmaps = self.constant_parameters.mipmaps_enabled )
1129 else:
1130 max_dim = gl.glGetIntegerv( gl.GL_MAX_TEXTURE_SIZE )
1131 resized = 0
1132 while max(p.texture.size) > max_dim:
1133 p.texture.make_half_size()
1134 resized = 1
1135 loaded_ok = 0
1136 while not loaded_ok:
1137 try:
1138
1139 p.texture.load( self.texture_object,
1140 internal_format = self.constant_parameters.internal_format,
1141 build_mipmaps = self.constant_parameters.mipmaps_enabled )
1142 except TextureTooLargeError:
1143 p.texture.make_half_size()
1144 resized = 1
1145 else:
1146 loaded_ok = 1
1147 if resized:
1148 logger = logging.getLogger('VisionEgg.Textures')
1149 logger.warning("Resized texture in %s to %d x %d"%(
1150 str(self),p.texture.size[0],p.texture.size[1]))
1151
1152 -class Mask2D(VisionEgg.ClassWithParameters):
1153 """A mask for windowing a portion of a texture.
1154
1155 Thanks to the author, Jon Peirce, of the AlphaStim class from the
1156 PsychoPy package from which the idea to do this came.
1157
1158 Constant Parameters
1159 ===================
1160 function -- 'gaussian' or 'circle' (String)
1161 Default: gaussian
1162 num_samples -- size of mask texture data (units: number of texels) (Sequence2 of Real)
1163 Default: (256, 256)
1164 radius_parameter -- radius for circle, sigma for gaussian (Real)
1165 Default: 25.0
1166 """
1167
1168
1169 constant_parameters_and_defaults = {
1170 'function':('gaussian',
1171 ve_types.String,
1172 "'gaussian' or 'circle'"),
1173 'radius_parameter':(25.0,
1174 ve_types.Real,
1175 "radius for circle, sigma for gaussian"),
1176 'num_samples':((256,256),
1177 ve_types.Sequence2(ve_types.Real),
1178 "size of mask texture data (units: number of texels)"),
1179 }
1181 VisionEgg.ClassWithParameters.__init__(self,**kw)
1182
1183 cp = self.constant_parameters
1184 width,height = cp.num_samples
1185 if width != next_power_of_2(width):
1186 raise RuntimeError("Mask must have width num_samples power of 2")
1187 if height != next_power_of_2(height):
1188 raise RuntimeError("Mask must have height num_samples power of 2")
1189
1190 gl.glActiveTextureARB(gl.GL_TEXTURE1_ARB)
1191 self.texture_object = TextureObject(dimensions=2)
1192
1193 if cp.function == "gaussian":
1194 xx = numpyNumeric.outerproduct(numpyNumeric.ones((1,cp.num_samples[1])),
1195 numpyNumeric.arange(0,cp.num_samples[0],1.0)-cp.num_samples[0]/2)
1196 yy = numpyNumeric.outerproduct(numpyNumeric.arange(0,cp.num_samples[1],1.0)-cp.num_samples[1]/2,
1197 numpyNumeric.ones((1,cp.num_samples[0])))
1198 dist_from_center = numpyNumeric.sqrt(xx**2 + yy**2)
1199 sigma = cp.radius_parameter
1200 data = numpyNumeric.exp( -dist_from_center**2.0 / (2.0*sigma**2.0) )
1201 elif cp.function == "circle":
1202 data = numpyNumeric.zeros(cp.num_samples,numpyNumeric.Float)
1203
1204
1205 oversamples = 4
1206 x_offsets = numpyNumeric.arange(0.0,1.0,1.0/oversamples)
1207 x_offsets = x_offsets - MLab.mean(x_offsets)
1208 y_offsets = x_offsets
1209 for x_offset in x_offsets:
1210 for y_offset in y_offsets:
1211 xx = numpyNumeric.outerproduct(numpyNumeric.ones((1,cp.num_samples[1])),
1212 numpyNumeric.arange(0,cp.num_samples[0],1.0)-cp.num_samples[0]/2+0.5)+x_offset
1213 yy = numpyNumeric.outerproduct(numpyNumeric.arange(0,cp.num_samples[1],1.0)-cp.num_samples[1]/2+0.5,
1214 numpyNumeric.ones((1,cp.num_samples[0])))+y_offset
1215 dist_from_center = numpyNumeric.sqrt(xx**2 + yy**2)
1216 data += dist_from_center <= cp.radius_parameter
1217 data = data / float( len(x_offsets)*len(y_offsets) )
1218 else:
1219 raise RuntimeError("Don't know about window function %s"%self.constant_parameters.function)
1220
1221 self.texture_object.put_new_image(data,
1222 data_format=gl.GL_ALPHA,
1223 internal_format=gl.GL_ALPHA)
1224 self.texture_object.set_min_filter(gl.GL_LINEAR)
1225 self.texture_object.set_wrap_mode_s(gl.GL_CLAMP_TO_EDGE)
1226 self.texture_object.set_wrap_mode_t(gl.GL_CLAMP_TO_EDGE)
1227 gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_MODULATE)
1228
1229
1230 gl.glActiveTextureARB(gl.GL_TEXTURE0_ARB)
1231
1233
1234
1235
1236
1237
1238 gl.glActiveTextureARB(gl.GL_TEXTURE1_ARB)
1239 gl.glBindTexture(gl.GL_TEXTURE_2D, self.texture_object.gl_id)
1240 gl.glEnable(gl.GL_TEXTURE_2D)
1241
1242
1243 gl.glBegin(gl.GL_QUADS)
1244
1245 gl.glMultiTexCoord2fARB(gl.GL_TEXTURE0_ARB,lt,bt)
1246 gl.glMultiTexCoord2fARB(gl.GL_TEXTURE1_ARB,0.0,0.0)
1247 gl.glVertex3f(*v1)
1248
1249 gl.glMultiTexCoord2fARB(gl.GL_TEXTURE0_ARB,rt,bt)
1250 gl.glMultiTexCoord2fARB(gl.GL_TEXTURE1_ARB,1.0,0.0)
1251 gl.glVertex3f(*v2)
1252
1253 gl.glMultiTexCoord2fARB(gl.GL_TEXTURE0_ARB,rt,tt)
1254 gl.glMultiTexCoord2fARB(gl.GL_TEXTURE1_ARB,1.0,1.0)
1255 gl.glVertex3f(*v3)
1256
1257 gl.glMultiTexCoord2fARB(gl.GL_TEXTURE0_ARB,lt,tt)
1258 gl.glMultiTexCoord2fARB(gl.GL_TEXTURE1_ARB,0.0,1.0)
1259 gl.glVertex3f(*v4)
1260
1261 gl.glEnd()
1262 gl.glDisable(gl.GL_TEXTURE_2D)
1263 gl.glActiveTextureARB(gl.GL_TEXTURE0_ARB)
1264
1266
1267
1268
1269 v1 = (le,be,depth)
1270 v2 = (re,be,depth)
1271 v3 = (re,te,depth)
1272 v4 = (le,te,depth)
1273 self.draw_masked_quad_3d(lt,rt,bt,tt,v1,v2,v3,v4)
1274
1275 -class TextureStimulus(TextureStimulusBaseClass):
1276 """A textured rectangle.
1277
1278 This is mainly for 2D use (z coordinate fixed to 0.0 and w
1279 coordinated fixed to 1.0 if not given).
1280
1281
1282 Parameters
1283 ==========
1284 anchor -- specifies how position parameter is interpreted (String)
1285 Default: lowerleft
1286 angle -- units: degrees, 0=right, 90=up (Real)
1287 Default: 0.0
1288 color -- texture environment color. alpha ignored (if given) for max_alpha parameter (AnyOf(Sequence3 of Real or Sequence4 of Real))
1289 Default: (1.0, 1.0, 1.0)
1290 depth_test -- perform depth test? (Boolean)
1291 Default: False
1292 mask -- optional masking function (Instance of <class 'VisionEgg.Textures.Mask2D'>)
1293 Default: (determined at runtime)
1294 max_alpha -- controls opacity. 1.0=copletely opaque, 0.0=completely transparent (Real)
1295 Default: 1.0
1296 on -- draw stimulus? (Boolean)
1297 Default: True
1298 position -- units: eye coordinates (AnyOf(Sequence2 of Real or Sequence3 of Real or Sequence4 of Real))
1299 Default: (0.0, 0.0)
1300 size -- defaults to texture data size (units: eye coordinates) (Sequence2 of Real)
1301 Default: (determined at runtime)
1302 texture -- source of texture data (Instance of <class 'VisionEgg.Textures.Texture'>)
1303 Inherited from TextureStimulusBaseClass
1304 Default: (determined at runtime)
1305 texture_mag_filter -- OpenGL filter enum (Integer)
1306 Inherited from TextureStimulusBaseClass
1307 Default: GL_LINEAR (9729)
1308 texture_min_filter -- OpenGL filter enum (Integer)
1309 Inherited from TextureStimulusBaseClass
1310 Default: (GL enum determined at runtime)
1311 texture_wrap_s -- OpenGL texture wrap enum (Integer)
1312 Inherited from TextureStimulusBaseClass
1313 Default: (GL enum determined at runtime)
1314 texture_wrap_t -- OpenGL texture wrap enum (Integer)
1315 Inherited from TextureStimulusBaseClass
1316 Default: (GL enum determined at runtime)
1317
1318 Constant Parameters
1319 ===================
1320 internal_format -- format with which OpenGL uses texture data (OpenGL data type enum) (Integer)
1321 Default: GL_RGB (6407)
1322 mipmaps_enabled -- Are mipmaps enabled? (Boolean)
1323 Default: True
1324 shrink_texture_ok -- Allow automatic shrinking of texture if too big? (Boolean)
1325 Default: False
1326 """
1327
1328 parameters_and_defaults = {
1329 'on':(True,
1330 ve_types.Boolean,
1331 "draw stimulus?"),
1332 'mask':(None,
1333 ve_types.Instance(Mask2D),
1334 "optional masking function"),
1335 'position':((0.0,0.0),
1336 ve_types.AnyOf(ve_types.Sequence2(ve_types.Real),
1337 ve_types.Sequence3(ve_types.Real),
1338 ve_types.Sequence4(ve_types.Real)),
1339 "units: eye coordinates"),
1340 'anchor':('lowerleft',
1341 ve_types.String,
1342 "specifies how position parameter is interpreted"),
1343 'lowerleft':(None,
1344 ve_types.Sequence2(ve_types.Real),
1345 "",
1346 VisionEgg.ParameterDefinition.DEPRECATED),
1347 'angle':(0.0,
1348 ve_types.Real,
1349 "units: degrees, 0=right, 90=up"),
1350 'size':(None,
1351 ve_types.Sequence2(ve_types.Real),
1352 "defaults to texture data size (units: eye coordinates)"),
1353 'max_alpha':(1.0,
1354 ve_types.Real,
1355 "controls opacity. 1.0=copletely opaque, 0.0=completely transparent"),
1356 'color':((1.0,1.0,1.0),
1357 ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
1358 ve_types.Sequence4(ve_types.Real)),
1359 "texture environment color. alpha ignored (if given) for max_alpha parameter"),
1360 'depth_test':(False,
1361 ve_types.Boolean,
1362 "perform depth test?"),
1363 }
1364
1366 p = self.parameters
1367 if p.texture != self._using_texture:
1368 self._reload_texture()
1369 if p.lowerleft != None:
1370 if not hasattr(VisionEgg.config,"_GAVE_LOWERLEFT_DEPRECATION"):
1371 logger = logging.getLogger('VisionEgg.Textures')
1372 logger.warning("Specifying texture by 'lowerleft' "
1373 "deprecated parameter deprecated. Use "
1374 "'position' parameter instead. (Allows "
1375 "use of 'anchor' parameter to set to "
1376 "other values.)")
1377 VisionEgg.config._GAVE_LOWERLEFT_DEPRECATION = 1
1378 p.anchor = 'lowerleft'
1379 p.position = p.lowerleft[0], p.lowerleft[1]
1380 if p.on:
1381 tex = p.texture
1382
1383 if p.size is None:
1384
1385
1386 size = tex.size
1387 else:
1388 size = p.size
1389
1390
1391 lowerleft = VisionEgg._get_lowerleft(p.position,p.anchor,size)
1392
1393
1394 gl.glMatrixMode(gl.GL_MODELVIEW)
1395 gl.glPushMatrix()
1396 try:
1397 if p.depth_test:
1398 gl.glEnable(gl.GL_DEPTH_TEST)
1399 else:
1400 gl.glDisable(gl.GL_DEPTH_TEST)
1401 gl.glEnable( gl.GL_TEXTURE_2D )
1402
1403
1404 gl.glEnable( gl.GL_BLEND )
1405 gl.glBlendFunc( gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA )
1406
1407 if not self.constant_parameters.mipmaps_enabled:
1408 if p.texture_min_filter in TextureStimulusBaseClass._mipmap_modes:
1409 raise RuntimeError("Specified a mipmap mode in texture_min_filter, but mipmaps not enabled.")
1410 self.texture_object.set_min_filter( p.texture_min_filter )
1411 self.texture_object.set_mag_filter( p.texture_mag_filter )
1412 self.texture_object.set_wrap_mode_s( p.texture_wrap_s )
1413 self.texture_object.set_wrap_mode_t( p.texture_wrap_t )
1414 gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_MODULATE)
1415
1416 translate_vector = p.position
1417 if len(translate_vector) == 2:
1418 translate_vector = translate_vector[0], translate_vector[1], 0
1419 gl.glTranslate(*translate_vector)
1420 gl.glRotate(p.angle,0,0,1)
1421
1422 gl.glColor4f(p.color[0],p.color[1],p.color[2],p.max_alpha)
1423
1424
1425 if numpy.alltrue( numpy.isfinite( numpy.concatenate((lowerleft,p.position)) ) ):
1426 l = lowerleft[0] - p.position[0]
1427 r = l + size[0]
1428 b = lowerleft[1] - p.position[1]
1429 t = b + size[1]
1430
1431 tex.update()
1432
1433 if p.mask:
1434 p.mask.draw_masked_quad(tex.buf_lf,tex.buf_rf,tex.buf_bf,tex.buf_tf,
1435 l,r,b,t,0.0)
1436 else:
1437 gl.glBegin(gl.GL_QUADS)
1438 gl.glTexCoord2f(tex.buf_lf,tex.buf_bf)
1439 gl.glVertex2f(l,b)
1440
1441 gl.glTexCoord2f(tex.buf_rf,tex.buf_bf)
1442 gl.glVertex2f(r,b)
1443
1444 gl.glTexCoord2f(tex.buf_rf,tex.buf_tf)
1445 gl.glVertex2f(r,t)
1446
1447 gl.glTexCoord2f(tex.buf_lf,tex.buf_tf)
1448 gl.glVertex2f(l,t)
1449 gl.glEnd()
1450 finally:
1451 gl.glPopMatrix()
1452
1453 -class TextureStimulus3D(TextureStimulusBaseClass):
1454 """A textured rectangle placed arbitrarily in 3 space.
1455
1456 Parameters
1457 ==========
1458 depth_test -- perform depth test? (Boolean)
1459 Default: True
1460 lowerleft -- vertex position (units: eye coordinates) (AnyOf(Sequence3 of Real or Sequence4 of Real))
1461 Default: (0.0, 0.0, -1.0)
1462 lowerright -- vertex position (units: eye coordinates) (AnyOf(Sequence3 of Real or Sequence4 of Real))
1463 Default: (1.0, 0.0, -1.0)
1464 on -- (Boolean)
1465 Default: True
1466 texture -- source of texture data (Instance of <class 'VisionEgg.Textures.Texture'>)
1467 Inherited from TextureStimulusBaseClass
1468 Default: (determined at runtime)
1469 texture_mag_filter -- OpenGL filter enum (Integer)
1470 Inherited from TextureStimulusBaseClass
1471 Default: GL_LINEAR (9729)
1472 texture_min_filter -- OpenGL filter enum (Integer)
1473 Inherited from TextureStimulusBaseClass
1474 Default: (GL enum determined at runtime)
1475 texture_wrap_s -- OpenGL texture wrap enum (Integer)
1476 Inherited from TextureStimulusBaseClass
1477 Default: (GL enum determined at runtime)
1478 texture_wrap_t -- OpenGL texture wrap enum (Integer)
1479 Inherited from TextureStimulusBaseClass
1480 Default: (GL enum determined at runtime)
1481 upperleft -- vertex position (units: eye coordinates) (AnyOf(Sequence3 of Real or Sequence4 of Real))
1482 Default: (0.0, 1.0, -1.0)
1483 upperright -- vertex position (units: eye coordinates) (AnyOf(Sequence3 of Real or Sequence4 of Real))
1484 Default: (1.0, 1.0, -1.0)
1485
1486 Constant Parameters
1487 ===================
1488 internal_format -- format with which OpenGL uses texture data (OpenGL data type enum) (Integer)
1489 Default: GL_RGB (6407)
1490 mipmaps_enabled -- Are mipmaps enabled? (Boolean)
1491 Default: True
1492 shrink_texture_ok -- Allow automatic shrinking of texture if too big? (Boolean)
1493 Default: False
1494 """
1495
1496 parameters_and_defaults = {'on':(True,
1497 ve_types.Boolean),
1498 'lowerleft':((0.0,0.0,-1.0),
1499 ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
1500 ve_types.Sequence4(ve_types.Real)),
1501 "vertex position (units: eye coordinates)"),
1502 'lowerright':((1.0,0.0,-1.0),
1503 ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
1504 ve_types.Sequence4(ve_types.Real)),
1505 "vertex position (units: eye coordinates)"),
1506 'upperleft':((0.0,1.0,-1.0),
1507 ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
1508 ve_types.Sequence4(ve_types.Real)),
1509 "vertex position (units: eye coordinates)"),
1510 'upperright':((1.0,1.0,-1.0),
1511 ve_types.AnyOf(ve_types.Sequence3(ve_types.Real),
1512 ve_types.Sequence4(ve_types.Real)),
1513 "vertex position (units: eye coordinates)"),
1514 'depth_test':(True,
1515 ve_types.Boolean,
1516 "perform depth test?"),
1517 }
1518
1520 p = self.parameters
1521 if p.texture != self._using_texture:
1522 self._reload_texture()
1523 if p.on:
1524 if p.depth_test:
1525 gl.glEnable(gl.GL_DEPTH_TEST)
1526 else:
1527 gl.glDisable(gl.GL_DEPTH_TEST)
1528
1529 gl.glEnable(gl.GL_TEXTURE_2D)
1530 gl.glBindTexture(gl.GL_TEXTURE_2D,self.texture_object.gl_id)
1531
1532 if not self.constant_parameters.mipmaps_enabled:
1533 if p.texture_min_filter in TextureStimulusBaseClass._mipmap_modes:
1534 raise RuntimeError("Specified a mipmap mode in texture_min_filter, but mipmaps not enabled.")
1535 self.texture_object.set_min_filter( p.texture_min_filter )
1536 self.texture_object.set_mag_filter( p.texture_mag_filter )
1537 self.texture_object.set_wrap_mode_s( p.texture_wrap_s )
1538 self.texture_object.set_wrap_mode_t( p.texture_wrap_t )
1539
1540
1541 gl.glEnable( gl.GL_BLEND )
1542 gl.glBlendFunc( gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA )
1543 gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_MODULATE)
1544
1545 tex = self.parameters.texture
1546 tex.update()
1547
1548 gl.glBegin(gl.GL_QUADS)
1549 gl.glTexCoord2f(tex.buf_lf,tex.buf_bf)
1550 gl.glVertex(*p.lowerleft)
1551
1552 gl.glTexCoord2f(tex.buf_rf,tex.buf_bf)
1553 gl.glVertex(*p.lowerright)
1554
1555 gl.glTexCoord2f(tex.buf_rf,tex.buf_tf)
1556 gl.glVertex(*p.upperright)
1557
1558 gl.glTexCoord2f(tex.buf_lf,tex.buf_tf)
1559 gl.glVertex(*p.upperleft)
1560 gl.glEnd()
1561
1562
1563
1564
1565
1566
1567
1569 """Panoramic image texture mapped onto flat rectangle or 3D cylinder.
1570
1571
1572 Parameters
1573 ==========
1574 anchor -- only used when flat: same as anchor parameter of TextureStimulus (String)
1575 Default: center
1576 angular_position -- may be best to clamp in range [0.0,360.0] (Real)
1577 Default: 0.0
1578 contrast -- (Real)
1579 Default: 1.0
1580 drum_center_azimuth -- changes orientation of drum in space (Real)
1581 Default: 0.0
1582 drum_center_elevation -- changes orientation of drum in space (Real)
1583 Default: 0.0
1584 flat -- toggles flat vs. cylinder (Boolean)
1585 Default: False
1586 flat_size -- defaults to texture data size (units: eye coordinates) (Sequence2 of Real)
1587 Default: (determined at runtime)
1588 flip_image -- toggles normal vs. horizonally flipped image (Boolean)
1589 Default: False
1590 height -- height of cyliner, automatically set by texel aspect ratio if < 0. (Real)
1591 Default: -1
1592 num_sides -- (UnsignedInteger)
1593 Default: 50
1594 on -- (Boolean)
1595 Default: True
1596 orientation -- 0=right, 90=up (Real)
1597 Default: 0.0
1598 position -- 3D: position of drum center, 2D (flat): same as position parameter for TextureStimulus (AnyOf(Sequence2 of Real or Sequence3 of Real))
1599 Default: (0.0, 0.0, 0.0)
1600 radius -- radius if cylinder (not used if flat) (Real)
1601 Default: 1.0
1602 texture -- source of texture data (Instance of <class 'VisionEgg.Textures.Texture'>)
1603 Inherited from TextureStimulusBaseClass
1604 Default: (determined at runtime)
1605 texture_mag_filter -- OpenGL filter enum (Integer)
1606 Inherited from TextureStimulusBaseClass
1607 Default: GL_LINEAR (9729)
1608 texture_min_filter -- OpenGL filter enum (Integer)
1609 Inherited from TextureStimulusBaseClass
1610 Default: (GL enum determined at runtime)
1611 texture_wrap_s -- OpenGL texture wrap enum (Integer)
1612 Inherited from TextureStimulusBaseClass
1613 Default: (GL enum determined at runtime)
1614 texture_wrap_t -- OpenGL texture wrap enum (Integer)
1615 Inherited from TextureStimulusBaseClass
1616 Default: (GL enum determined at runtime)
1617
1618 Constant Parameters
1619 ===================
1620 internal_format -- format with which OpenGL uses texture data (OpenGL data type enum) (Integer)
1621 Default: GL_RGB (6407)
1622 mipmaps_enabled -- Are mipmaps enabled? (Boolean)
1623 Default: True
1624 shrink_texture_ok -- Allow automatic shrinking of texture if too big? (Boolean)
1625 Default: False
1626 """
1627
1628 parameters_and_defaults = {
1629 'on':(True,
1630 ve_types.Boolean),
1631 'height':(-1,
1632 ve_types.Real,
1633 'height of cyliner, automatically set by texel aspect ratio if < 0.',
1634 ),
1635 'num_sides':(50,
1636 ve_types.UnsignedInteger),
1637 'angular_position':(0.0,
1638 ve_types.Real,
1639 'may be best to clamp in range [0.0,360.0]'),
1640 'contrast':(1.0,
1641 ve_types.Real),
1642 'flat':(False,
1643 ve_types.Boolean,
1644 'toggles flat vs. cylinder'),
1645 'flat_size':(None,
1646 ve_types.Sequence2(ve_types.Real),
1647 "defaults to texture data size (units: eye coordinates)"),
1648 'flip_image':(False,
1649 ve_types.Boolean,
1650 'toggles normal vs. horizonally flipped image'),
1651 'radius':(1.0,
1652 ve_types.Real,
1653 'radius if cylinder (not used if flat)'),
1654 'position':( (0.0,0.0,0.0),
1655 ve_types.AnyOf(ve_types.Sequence2(ve_types.Real),
1656 ve_types.Sequence3(ve_types.Real)),
1657 '3D: position of drum center, 2D (flat): same as position parameter for TextureStimulus'),
1658 'anchor':( 'center',
1659 ve_types.String,
1660 'only used when flat: same as anchor parameter of TextureStimulus',
1661 ),
1662 'drum_center_azimuth':(0.0,
1663 ve_types.Real,
1664 'changes orientation of drum in space',
1665 ),
1666 'drum_center_elevation':(0.0,
1667 ve_types.Real,
1668 'changes orientation of drum in space'),
1669 'orientation':(0.0,
1670 ve_types.Real,
1671 '0=right, 90=up'),
1672 }
1673
1674 __slots__ = (
1675 'cached_display_list_normal',
1676 'cached_display_list_mirror',
1677 'cached_display_list_num_sides',
1678 'cached_display_list_radius',
1679 'cached_display_list_height',
1680 'texture_stimulus',
1681 )
1682
1688
1690 """Redraw the stimulus on every frame.
1691 """
1692 p = self.parameters
1693 if p.texture != self._using_texture:
1694 self._reload_texture()
1695 self.rebuild_display_list()
1696 if p.on:
1697
1698 gl.glEnable( gl.GL_DEPTH_TEST )
1699 gl.glEnable( gl.GL_TEXTURE_2D )
1700 gl.glEnable( gl.GL_BLEND )
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718 gl.glBlendFunc( gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA )
1719
1720 gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_DECAL)
1721
1722
1723 gl.glMatrixMode(gl.GL_MODELVIEW)
1724 gl.glPushMatrix()
1725 try:
1726 gl.glColor4f(0.5,0.5,0.5,p.contrast)
1727
1728 if not self.constant_parameters.mipmaps_enabled:
1729 if p.texture_min_filter in TextureStimulusBaseClass._mipmap_modes:
1730 raise RuntimeError("Specified a mipmap mode in texture_min_filter, but mipmaps not enabled.")
1731 self.texture_object.set_min_filter( p.texture_min_filter )
1732 self.texture_object.set_mag_filter( p.texture_mag_filter )
1733 self.texture_object.set_wrap_mode_s( p.texture_wrap_s )
1734 self.texture_object.set_wrap_mode_t( p.texture_wrap_t )
1735
1736 if p.flat:
1737 lowerleft = VisionEgg._get_lowerleft(p.position,p.anchor,p.texture.size)
1738
1739 translate_vector = p.position
1740 if len(translate_vector) == 2:
1741 translate_vector = translate_vector[0], translate_vector[1], 0
1742 gl.glTranslate(*translate_vector)
1743 gl.glRotatef(p.orientation,0,0,1)
1744
1745 if p.flip_image:
1746 raise NotImplementedError("flip_image not yet supported for flat spinning drums.")
1747 w,h = p.texture.size
1748
1749
1750 tex_phase = p.angular_position/360.0 + 0.5
1751 tex_phase = tex_phase % 1.0
1752
1753 TINY = 1.0e-10
1754 tex = p.texture
1755 tex.update()
1756
1757 if p.flat_size is None:
1758 size = tex.size
1759 else:
1760 size = p.flat_size
1761
1762 l = lowerleft[0] - p.position[0]
1763 r = l + size[0]
1764 b = lowerleft[1] - p.position[1]
1765 t = b + size[1]
1766
1767
1768 if tex_phase < TINY:
1769
1770 gl.glBegin(gl.GL_QUADS)
1771 gl.glTexCoord2f(tex.buf_lf,tex.buf_bf)
1772 gl.glVertex2f(l,b)
1773
1774 gl.glTexCoord2f(tex.buf_rf,tex.buf_bf)
1775 gl.glVertex2f(r,b)
1776
1777 gl.glTexCoord2f(tex.buf_rf,tex.buf_tf)
1778 gl.glVertex2f(r,t)
1779
1780 gl.glTexCoord2f(tex.buf_lf,tex.buf_tf)
1781 gl.glVertex2f(l,t)
1782 gl.glEnd()
1783
1784 else:
1785
1786 buf_break_f = ( (tex.buf_rf - tex.buf_lf) * (1.0-tex_phase) ) + tex.buf_lf
1787
1788
1789 quad_x_break = (r-l) * tex_phase + l
1790
1791
1792 gl.glBegin(gl.GL_QUADS)
1793
1794
1795
1796 gl.glTexCoord2f(buf_break_f,tex.buf_bf)
1797 gl.glVertex2f(l,b)
1798
1799 gl.glTexCoord2f(tex.buf_rf,tex.buf_bf)
1800 gl.glVertex2f(quad_x_break,b)
1801
1802 gl.glTexCoord2f(tex.buf_rf,tex.buf_tf)
1803 gl.glVertex2f(quad_x_break,t)
1804
1805 gl.glTexCoord2f(buf_break_f,tex.buf_tf)
1806 gl.glVertex2f(l,t)
1807
1808
1809
1810 gl.glTexCoord2f(tex.buf_lf,tex.buf_bf)
1811 gl.glVertex2f(quad_x_break,b)
1812
1813 gl.glTexCoord2f(buf_break_f,tex.buf_bf)
1814 gl.glVertex2f(r,b)
1815
1816 gl.glTexCoord2f(buf_break_f,tex.buf_tf)
1817 gl.glVertex2f(r,t)
1818
1819 gl.glTexCoord2f(tex.buf_lf,tex.buf_tf)
1820 gl.glVertex2f(quad_x_break,t)
1821 gl.glEnd()
1822
1823 else:
1824 gl.glTranslatef(p.position[0],p.position[1],p.position[2])
1825
1826
1827 gl.glRotatef(p.drum_center_azimuth,0,-1,0)
1828 gl.glRotatef(p.drum_center_elevation,1,0,0)
1829
1830
1831 gl.glRotatef(p.orientation,0,0,1)
1832
1833
1834
1835 gl.glRotatef(p.angular_position,0,-1,0)
1836
1837 if ((p.num_sides != self.cached_display_list_num_sides) or
1838 (p.radius != self.cached_display_list_radius) or
1839 (p.height != self.cached_display_list_height)):
1840 self.rebuild_display_list()
1841
1842 if not p.flip_image:
1843 gl.glCallList(self.cached_display_list_normal)
1844 else:
1845 gl.glCallList(self.cached_display_list_mirror)
1846 finally:
1847 gl.glMatrixMode(gl.GL_MODELVIEW)
1848 gl.glPopMatrix()
1849
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860 r = self.parameters.radius
1861 circum = 2.0*math.pi*r
1862 tex = self.parameters.texture
1863 if self.parameters.height < 0:
1864 h = circum/float(tex.size[0])*float(tex.size[1])/2.0
1865 else:
1866 h = self.parameters.height
1867
1868 num_sides = self.parameters.num_sides
1869 self.cached_display_list_num_sides = num_sides
1870 self.cached_display_list_radius = r
1871 self.cached_display_list_height = self.parameters.height
1872
1873 deltaTheta = 2.0*math.pi / num_sides
1874 for direction in ['normal','mirror']:
1875 if direction == 'normal':
1876 gl.glNewList(self.cached_display_list_normal,gl.GL_COMPILE)
1877 else:
1878 gl.glNewList(self.cached_display_list_mirror,gl.GL_COMPILE)
1879 gl.glBegin(gl.GL_QUADS)
1880 for i in range(num_sides):
1881
1882 theta1 = i*deltaTheta
1883 theta2 = (i+1)*deltaTheta
1884
1885 if direction == 'normal':
1886 frac1 = (tex.buf_lf + (float(i)/num_sides*tex.size[0]))/float(tex.size[0])
1887 frac2 = (tex.buf_lf + (float(i+1)/num_sides*tex.size[0]))/float(tex.size[0])
1888 else:
1889 j = num_sides-i-1
1890 frac1 = (tex.buf_lf + (float(j+1)/num_sides*tex.size[0]))/float(tex.size[0])
1891 frac2 = (tex.buf_lf + (float(j)/num_sides*tex.size[0]))/float(tex.size[0])
1892
1893 x1 = r*math.cos(theta1)
1894 z1 = r*math.sin(theta1)
1895 x2 = r*math.cos(theta2)
1896 z2 = r*math.sin(theta2)
1897
1898
1899 gl.glTexCoord2f(frac1, tex.buf_bf)
1900 gl.glVertex4f( x1, -h, z1, 1.0 )
1901
1902
1903 gl.glTexCoord2f(frac2, tex.buf_bf)
1904 gl.glVertex4f( x2, -h, z2, 1.0 )
1905
1906 gl.glTexCoord2f(frac2, tex.buf_tf);
1907 gl.glVertex4f( x2, h, z2, 1.0 )
1908
1909 gl.glTexCoord2f(frac1, tex.buf_tf)
1910 gl.glVertex4f( x1, h, z1, 1.0 )
1911 gl.glEnd()
1912 gl.glEndList()
1913
1915 """Cross useful for fixation point.
1916
1917 Parameters
1918 ==========
1919 on -- (Boolean)
1920 Default: True
1921 position -- (Sequence2 of Real)
1922 Default: (320, 240)
1923 size -- (Sequence2 of Real)
1924 Default: (64, 64)
1925
1926 Constant Parameters
1927 ===================
1928 texture_size -- (Sequence2 of Real)
1929 Default: (64, 64)
1930 """
1931
1932 parameters_and_defaults = {
1933 'on':(True,
1934 ve_types.Boolean),
1935 'position':((320,240),
1936 ve_types.Sequence2(ve_types.Real)),
1937 'size':((64,64),
1938 ve_types.Sequence2(ve_types.Real)),
1939 }
1940 constant_parameters_and_defaults = {
1941 'texture_size':((64,64),
1942 ve_types.Sequence2(ve_types.Real)),
1943 }
1944
1945 __slots__ = (
1946 'texture_stimulus',
1947 )
1948
1950 VisionEgg.Core.Stimulus.__init__(self,**kw)
1951 s = self.constant_parameters.texture_size
1952 mid_x = s[0]/2.0
1953 mid_y = s[1]/2.0
1954 texels = Image.new("RGBX",s,(0,0,0,0))
1955 texels_draw = ImageDraw.Draw(texels)
1956 texels_draw.rectangle( (mid_x-1, 0, mid_x+1, s[1]), fill=(0,0,0,255) )
1957 texels_draw.rectangle( (0, mid_y-1, s[0], mid_y+1), fill=(0,0,0,255) )
1958 texels_draw.line( (mid_x, 0, mid_x, s[1]), fill=(255,255,255,255) )
1959 texels_draw.line( (0, mid_y, s[0], mid_y), fill=(255,255,255,255) )
1960 self.texture_stimulus = TextureStimulus( texture = Texture(texels),
1961 position = self.parameters.position,
1962 anchor = 'center',
1963 size = self.parameters.size,
1964 internal_format = gl.GL_RGBA,
1965 mipmaps_enabled = False,
1966 texture_min_filter = gl.GL_NEAREST,
1967 texture_mag_filter = gl.GL_NEAREST,
1968 )
1970 contained = self.texture_stimulus.parameters
1971 my = self.parameters
1972 contained.position = my.position
1973 contained.size = my.size
1974 contained.on = my.on
1975 self.texture_stimulus.draw()
1976
1979