home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Multimedia / k3d-setup-0.7.11.0.exe / lib / site-packages / OpenGL / arrays / vbo.py < prev   
Encoding:
Python Source  |  2008-12-20  |  8.6 KB  |  257 lines

  1. """VertexBufferObject helper class"""
  2. from OpenGL import GL
  3. from OpenGL.arrays.arraydatatype import ArrayDatatype
  4. from OpenGL.arrays.formathandler import FormatHandler
  5. from OpenGL.GL.ARB import vertex_buffer_object
  6.  
  7.  
  8. import weakref
  9. __all__ = ('VBO','VBOHandler')
  10.  
  11. class Implementation( object ):
  12.     """Abstraction point for the various implementations that can be used
  13.     """
  14.     available = False
  15.     def _arbname( self, name ):
  16.         return (
  17.             (name.startswith( 'gl' ) and name.endswith( 'ARB' )) or 
  18.             (name.startswith( 'GL_' ) and name.endswith( 'ARB' ))
  19.         ) and (name != 'glInitVertexBufferObjectARB')
  20.     def basename( self, name ):
  21.         if name.endswith( '_ARB' ):
  22.             return name[:-4]
  23.         elif name.endswith( 'ARB' ):
  24.             return name[:-3]
  25.         else:
  26.             return name
  27.     def __init__( self ):
  28.         names = [name for name in dir(vertex_buffer_object) if self._arbname( name )]
  29.         if not GL.glBufferData:
  30.             for name in names:
  31.                 setattr( self, self.basename(name), getattr( vertex_buffer_object, name ))
  32.             self.available = True
  33.         elif vertex_buffer_object.glBufferDataARB:
  34.             for name in names:
  35.                 setattr( self, self.basename(name), getattr( GL, self.basename(name) ))
  36.             self.available = True
  37.     def __nonzero__( self ):
  38.         return self.available
  39.  
  40. def get_implementation( *args ):
  41.     """Retrieve the appropriate implementation for this machine"""
  42.     if VBO._I_ is None:
  43.         VBO.implementation = VBO._I_ = Implementation()
  44.     return VBO._I_
  45.  
  46.  
  47. class VBO( object ):
  48.     """Instances can be passed into array-handling routines
  49.     
  50.     You can check for whether VBOs are supported by accessing the implementation
  51.     attribute of the VBO, which will raise a RuntimeError if there is no available 
  52.     implementation.
  53.     """
  54.     _DELETERS_ = {}
  55.     copied = False
  56.     _no_cache_ = True # do not cache in context data arrays
  57.     def __init__( 
  58.         self, data, usage='GL_DYNAMIC_DRAW', 
  59.         target='GL_ARRAY_BUFFER',
  60.     ):
  61.         self.data = data 
  62.         self.usage = usage 
  63.         self.target = target 
  64.         self.buffers = []
  65.         self._copy_segments = []
  66.     _I_ = None
  67.     implementation = property( get_implementation, )
  68.     def resolve( self, value ):
  69.         """Resolve string constant to constant"""
  70.         if isinstance( value, (str,unicode)):
  71.             return getattr( self.implementation, self.implementation.basename( value ) )
  72.         return value
  73.     def set_array( self, data ):
  74.         """Update our entire array with new data"""
  75.         self.data = data 
  76.         self.copied = False
  77.     def __setitem__( self, slice, array):
  78.         """Set slice of data on the array and vbo (if copied already)
  79.         
  80.         slice -- the Python slice object determining how the data should 
  81.             be copied into the vbo/array 
  82.         array -- something array-compatible that will be used as the 
  83.             source of the data, note that the data-format will have to 
  84.             be the same as the internal data-array to work properly, if 
  85.             not, the amount of data copied will be wrong.
  86.         
  87.         This is a reasonably complex operation, it has to have all sorts
  88.         of state-aware changes to correctly map the source into the low-level
  89.         OpenGL view of the buffer (which is just bytes as far as the GL 
  90.         is concerned).
  91.         """
  92.         if slice.step and not slice.step == 1:
  93.             raise NotImplemented( """Don't know how to map stepped arrays yet""" )
  94.         # TODO: handle e.g. mapping character data into an integer data-set
  95.         data = ArrayDatatype.asArray( array )
  96.         start = (slice.start or 0) 
  97.         stop = (slice.stop or len(self.data))
  98.         if start < 0:
  99.             start += len(self.data)
  100.             start = max((start,0))
  101.         if stop < 0:
  102.             stop += len(self.data)
  103.             stop = max((stop,0))
  104.         self.data[ slice ] = data
  105.         if self.copied and self.buffers:
  106.             if start-stop != len(data):
  107.                 self.copied = False
  108.             elif start-stop == len(self.data):
  109.                 # re-copy the whole data-set
  110.                 self.copied = False
  111.             elif len(data):
  112.                 # now the fun part, we need to make the array match the 
  113.                 # structure of the array we're going to copy into and make 
  114.                 # the "size" parameter match the value we're going to copy in,
  115.                 # note that a 2D array (rather than a 1D array) may require 
  116.                 # multiple mappings to copy into the memory area...
  117.                 
  118.                 # find the step size from the dimensions and base size...
  119.                 size = ArrayDatatype.arrayByteCount( data ) / len(array)
  120.                 #baseSize = ArrayDatatype.unitSize( data )
  121.                 # now create the start and distance values...
  122.                 start *= size
  123.                 stop *= size
  124.                 # wait until the last moment (bind) to copy the data...
  125.                 self._copy_segments.append(
  126.                     (start,(stop-start), data)
  127.                 )
  128.     def __len__( self ):
  129.         return len( self.data )
  130.     def __getattr__( self, key ):
  131.         if key not in ('data','usage','target','buffers', 'copied','_I_','implementation','_copy_segments' ):
  132.             return getattr( self.data, key )
  133.         else:
  134.             raise AttributeError( key )
  135.     def create_buffers( self ):
  136.         """Create the internal buffer(s)"""
  137.         assert not self.buffers, """Already created the buffer"""
  138.         self.buffers = [ long(self.implementation.glGenBuffers(1)) ]
  139.         self.target = self.resolve( self.target )
  140.         self.usage = self.resolve( self.usage )
  141.         self._DELETERS_[ id(self) ] = weakref.ref( self, deleter( self.buffers, id(self), self.implementation ))
  142.         return self.buffers
  143.     def copy_data( self ):
  144.         """Copy our data into the buffer on the GL side"""
  145.         assert self.buffers, """Should do create_buffers before copy_data"""
  146.         if self.copied:
  147.             if self._copy_segments:
  148.                 while self._copy_segments:
  149.                     start,size,data  = self._copy_segments.pop(0)
  150.                     dataptr = ArrayDatatype.voidDataPointer( data )
  151.                     self.implementation.glBufferSubData(self.target, start, size, dataptr)
  152.         else:
  153.             self.implementation.glBufferData(
  154.                 self.target, 
  155.                 self.data,
  156.                 self.usage,
  157.             )
  158.             self.copied = True
  159.     def delete( self ):
  160.         """Delete this buffer explicitly"""
  161.         if self.buffers:
  162.             while self.buffers:
  163.                 try:
  164.                     self.implementation.glDeleteBuffers(1, self.buffers.pop(0))
  165.                 except AttributeError, err:
  166.                     pass
  167.     def bind( self ):
  168.         """Bind this buffer for use in vertex calls"""
  169.         if not self.buffers:
  170.             buffers = self.create_buffers()
  171.         self.implementation.glBindBuffer( self.target, self.buffers[0])
  172.         self.copy_data()
  173.     def unbind( self ):
  174.         """Unbind the buffer (make normal array operations active)"""
  175.         self.implementation.glBindBuffer( self.target,0 )
  176.     
  177.     def __add__( self, other ):
  178.         """Add an integer to this VBO (offset)"""
  179.         if hasattr( other, 'offset' ):
  180.             other = other.offset 
  181.         assert isinstance( other, int ), """Only know how to add integer offsets"""
  182.         return VBOOffset( self, other )
  183.  
  184. class VBOOffset( object ):
  185.     def __init__( self, vbo, offset ):
  186.         self.vbo = vbo 
  187.         self.offset = offset 
  188.     def __getattr__( self, key ):
  189.         if key != 'vbo':
  190.             return getattr( self.vbo, key )
  191.         raise AttributeError( 'No %r key in VBOOffset'%(key,))
  192.     def __add__( self, other ):
  193.         if hasattr( other, 'offset' ):
  194.             other = other.offset 
  195.         return VBOOffset( self.vbo, self.offset + other )
  196.  
  197. def deleter( buffers, key, implementation ):
  198.     """Produce a deleter callback to delete the given buffer"""
  199.     def doBufferDeletion( *args, **named ):
  200.         for buffer in buffers:
  201.             try:
  202.                 implementation.glDeleteBuffers(1, buffer)
  203.             except AttributeError, err:
  204.                 pass
  205.         try:
  206.             VBO._DELETERS_.pop( key )
  207.         except KeyError, err:
  208.             pass
  209.     return doBufferDeletion
  210.  
  211. class VBOHandler( FormatHandler ):
  212.     """Handles VBO instances passed in as array data"""
  213.     def dataPointer( self, instance ):
  214.         """Retrieve data-pointer from the instance's data
  215.         
  216.         Is always NULL, to indicate use of the bound pointer
  217.         """
  218.         return 0
  219.     def from_param( self, instance, typeCode=None ):
  220.         return ctypes.c_void_p( 0 )
  221.     def zeros( self, dims, typeCode ):
  222.         """Not implemented"""
  223.         raise NotImplemented( """Don't have VBO output support yet""" )
  224.     ones = zeros 
  225.     def asArray( self, value, typeCode=None ):
  226.         """Given a value, convert to array representation"""
  227.         return value
  228.     def arrayToGLType( self, value ):
  229.         """Given a value, guess OpenGL type of the corresponding pointer"""
  230.         return ArrayDatatype.arrayToGLType( value.data )
  231.     def arraySize( self, value, typeCode = None ):
  232.         """Given a data-value, calculate dimensions for the array"""
  233.         return ArrayDatatype.arraySize( value.data )
  234.     def unitSize( self, value, typeCode=None ):
  235.         """Determine unit size of an array (if possible)"""
  236.         return ArrayDatatype.unitSize( value.data )
  237.     def dimensions( self, value, typeCode=None ):
  238.         """Determine dimensions of the passed array value (if possible)"""
  239.         return ArrayDatatype.dimensions( value.data )
  240.  
  241. class VBOOffsetHandler( VBOHandler ):
  242.     def dataPointer( self, instance ):
  243.         """Retrieve data-pointer from the instance's data
  244.         
  245.         Is always NULL, to indicate use of the bound pointer
  246.         """
  247.         return instance.offset
  248.     def from_param( self, instance, typeCode=None ):
  249.         return ctypes.c_void_p( instance.offset )
  250.  
  251. HANDLER = VBOHandler()
  252. HANDLER.loadAll() # otherwise just the VBO would get loaded :)
  253. HANDLER.register( [ VBO ] )
  254.  
  255. HANDLER = VBOOffsetHandler()
  256. HANDLER.register( [ VBOOffset ] )
  257.