def current_program(): """ Return the currently bound shader program or None if there is None. The returned shader do not own the underlying buffer. """ cprog = GLint(0) glGetIntegerv(GL_CURRENT_PROGRAM, byref(cprog)) if cprog.value == 0: return None else: return ShaderProgram(cprog)
def get_info(): from OpenGL.GL import (glGetString, glGetIntegerv, GL_VENDOR, GL_VERSION, GL_SHADING_LANGUAGE_VERSION, GL_RENDERER, GL_MAJOR_VERSION, GL_MINOR_VERSION) return { 'major': glGetIntegerv(GL_MAJOR_VERSION), 'minor': glGetIntegerv(GL_MINOR_VERSION), 'vendor': glGetString(GL_VENDOR), 'version': glGetString(GL_VERSION), 'shader_language_version': glGetString(GL_SHADING_LANGUAGE_VERSION), 'renderer': glGetString(GL_RENDERER), }
def screen_shot(name="screen_shot.%03i.png"): """window screenshot.""" from OpenGL.GL import glGetIntegerv, glReadBuffer, glReadPixels from OpenGL.GL import GL_VIEWPORT, GL_READ_BUFFER, GL_FRONT, GL_RGB, GL_UNSIGNED_BYTE x, y, width, height = glGetIntegerv(GL_VIEWPORT) read_buffer = glGetIntegerv(GL_READ_BUFFER) glReadBuffer(GL_FRONT) data = glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE) glReadBuffer(read_buffer) from demos.common import png global _shot png.write(open(name % _shot, "wb"), width, height, 3, data) _shot += 1
def _Screen2Real(self, x, y, expZ=0.0): ''' Return the x and y in real coordination if the input is the x and y in screen coordination http://nehe.gamedev.net/article/using_gluunproject/16013/ Inputs: - x, y: coordinates get from mouse in screen coordination - expZ: expected Z of the real coordinates Outputs: coordinates in the real world ''' if not self.init: return 0, 0, 0 projection = glGetDoublev(GL_PROJECTION_MATRIX) modelview = glGetDoublev(GL_MODELVIEW_MATRIX) viewport = glGetIntegerv(GL_VIEWPORT) winX = float(x) winY = float(viewport[3]) - float(y) # winZ = glReadPixels(x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT) try: posXF, posYF, posZF = gluUnProject(winX, winY, 1, modelview, projection, viewport) posXN, posYN, posZN = gluUnProject(winX, winY, -1, modelview, projection, viewport) posZ = expZ posX = (posZ - posZN) / (posZF - posZN) * (posXF - posXN) + posXN posY = (posZ - posZN) / (posZF - posZN) * (posYF - posYN) + posYN # posX, posY, posZ = gluUnProject(winX, winY, winZ, modelview, projection, viewport) except: return 0, 0, 0 return posX, posY, posZ
def compile_total_list(self): if self.total_list is not None: glNewList(self.total_list, GL_COMPILE) # reset a few things glDisable(GL_DEPTH_TEST) glDisable(GL_LIGHTING) glMatrixMode(GL_PROJECTION) glLoadIdentity() glMatrixMode(GL_MODELVIEW) glLoadIdentity() # change the modelview to camera coordinates viewport = glGetIntegerv(GL_VIEWPORT) width = viewport[2] - viewport[0] height = viewport[3] - viewport[1] if width > height: w = float(width) / float(height) h = 1.0 else: w = 1.0 h = float(height) / float(width) glScalef(2 / w, 2 / h, 1.0) glCallList(self.draw_list) glEnable(GL_DEPTH_TEST) glEnable(GL_LIGHTING) glEndList()
def pickup(event, right): """ Function to get the Name Stack right es si el boton pulsado es el derecho El viewport[3]-event.y() creo que es porque la medida de las Y está invertida Los problemas que tenía con windows de tener que hacer varios click se corrigieron sustituyendo la región de selección de 1,1 a 5,5 """ viewport = glGetIntegerv(GL_VIEWPORT) glMatrixMode(GL_PROJECTION) glPushMatrix() glSelectBuffer(512) glRenderMode(GL_SELECT) glLoadIdentity() gluPickMatrix(event.x(), viewport[3] - event.y(), 4, 4, viewport) aspect = viewport[2] / viewport[3] gluPerspective(60, aspect, 1.0, 400) glMatrixMode(GL_MODELVIEW) self.paintGL() glMatrixMode(GL_PROJECTION) if right == False: parseLeftButtonNameStack(glRenderMode(GL_RENDER)) else: parseRightButtonNameStack(glRenderMode(GL_RENDER)) glPopMatrix() glMatrixMode(GL_MODELVIEW)
def _gl_select_(self, x, y): ''' ''' _gw, gh = self.GetSize() self.draw(offscreen=True) b = glReadPixels(x, gh - y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE) _buff = glSelectBuffer(128) view = glGetIntegerv(GL_VIEWPORT) glRenderMode(GL_SELECT) glInitNames() glPushName(0) glMatrixMode(GL_PROJECTION) glPushMatrix() glLoadIdentity() gluPickMatrix(x, gh - y, 1.0, 1.0, view) self._set_view_volume() glMatrixMode(GL_MODELVIEW) self.draw(offscreen=True) glMatrixMode(GL_PROJECTION) glPopMatrix() hits = glRenderMode(GL_RENDER) glMatrixMode(GL_MODELVIEW) self.scene_graph.set_view_cube_face(struct.unpack('BBB', b)) # get the top object return min([(h.near, h.names[0]) for h in hits])[1] if hits else None
def compile_total_list(self): if self.total_list is not None: glNewList(self.total_list, GL_COMPILE) # reset a few things glDisable(GL_DEPTH_TEST) glDisable(GL_LIGHTING) glMatrixMode(GL_PROJECTION) glLoadIdentity() glMatrixMode(GL_MODELVIEW) glLoadIdentity() # change the modelview to camera coordinates viewport = glGetIntegerv(GL_VIEWPORT) width = viewport[2] - viewport[0] height = viewport[3] - viewport[1] if width > height: w = float(width) / float(height) h = 1.0 else: w = 1.0 h = float(height) / float(width) glScalef(2/w, 2/h, 1.0) glCallList(self.draw_list) glEnable(GL_DEPTH_TEST) glEnable(GL_LIGHTING) glEndList()
def draw(self): # this code is modified from GLPane.drawcompass glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadIdentity() glMatrixMode( GL_PROJECTION ) # WARNING: we're now in nonstandard matrixmode (for sake of gluPickMatrix and glOrtho -- needed??##k) glPushMatrix() glLoadIdentity() try: glpane = self.env.glpane ## aspect = 1.0 ###WRONG -- should use glpane.aspect (which exists as of 070919) aspect = glpane.aspect # revised by bruce 070919, UNTESTED corner = self.corner delegate = self.delegate ###e should get glpane to do this for us (ie call a method in it to do this if necessary) # (this code is copied from it) glselect = glpane.current_glselect if glselect: # print "%r (ipath %r) setting up gluPickMatrix" % (self, self.ipath) x, y, w, h = glselect gluPickMatrix( x, y, w, h, glGetIntegerv(GL_VIEWPORT) # k is this arg needed? it might be the default... ) # the first three cases here are still ###WRONG if corner == UPPER_RIGHT: glOrtho(-50 * aspect, 5.5 * aspect, -50, 5.5, -5, 500) # Upper Right elif corner == UPPER_LEFT: glOrtho(-5 * aspect, 50.5 * aspect, -50, 5.5, -5, 500) # Upper Left elif corner == LOWER_LEFT: glOrtho(-5 * aspect, 50.5 * aspect, -5, 50.5, -5, 500) # Lower Left else: ## glOrtho(-50*aspect, 5.5*aspect, -5, 50.5, -5, 500) # Lower Right ## glOrtho(-50*aspect, 0, 0, 50, -5, 500) # Lower Right [used now] -- x from -50*aspect to 0, y (bot to top) from 0 to 50 glOrtho(-glpane.width * PIXELS, 0, 0, glpane.height * PIXELS, -5, 500) # approximately right for the checkbox, but I ought to count pixels to be sure (note, PIXELS is a pretty inexact number) glMatrixMode(GL_MODELVIEW) ###k guess 061210 at possible bugfix (and obviously needed in general) -- # do this last to leave the matrixmode standard # (status of bugs & fixes unclear -- hard to test since even Highlightable(projection=True) w/o any change to # projection matrix (test _9cx) doesn't work!) offset = (-delegate.bright, delegate.bbottom) # only correct for LOWER_RIGHT glTranslatef(offset[0], offset[1], 0) self.drawkid(delegate) ## delegate.draw() finally: glMatrixMode(GL_PROJECTION) glPopMatrix() glMatrixMode(GL_MODELVIEW) # be sure to do this last, to leave the matrixmode standard glPopMatrix() return
def draw(self): # this code is modified from GLPane.drawcompass glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadIdentity() glMatrixMode(GL_PROJECTION) # WARNING: we're now in nonstandard matrixmode (for sake of gluPickMatrix and glOrtho -- needed??##k) glPushMatrix() glLoadIdentity() try: glpane = self.env.glpane ## aspect = 1.0 ###WRONG -- should use glpane.aspect (which exists as of 070919) aspect = glpane.aspect # revised by bruce 070919, UNTESTED corner = self.corner delegate = self.delegate ###e should get glpane to do this for us (ie call a method in it to do this if necessary) # (this code is copied from it) glselect = glpane.current_glselect if glselect: # print "%r (ipath %r) setting up gluPickMatrix" % (self, self.ipath) x,y,w,h = glselect gluPickMatrix( x,y, w,h, glGetIntegerv( GL_VIEWPORT ) #k is this arg needed? it might be the default... ) # the first three cases here are still ###WRONG if corner == UPPER_RIGHT: glOrtho(-50*aspect, 5.5*aspect, -50, 5.5, -5, 500) # Upper Right elif corner == UPPER_LEFT: glOrtho(-5*aspect, 50.5*aspect, -50, 5.5, -5, 500) # Upper Left elif corner == LOWER_LEFT: glOrtho(-5*aspect, 50.5*aspect, -5, 50.5, -5, 500) # Lower Left else: ## glOrtho(-50*aspect, 5.5*aspect, -5, 50.5, -5, 500) # Lower Right ## glOrtho(-50*aspect, 0, 0, 50, -5, 500) # Lower Right [used now] -- x from -50*aspect to 0, y (bot to top) from 0 to 50 glOrtho(-glpane.width * PIXELS, 0, 0, glpane.height * PIXELS, -5, 500) # approximately right for the checkbox, but I ought to count pixels to be sure (note, PIXELS is a pretty inexact number) glMatrixMode(GL_MODELVIEW) ###k guess 061210 at possible bugfix (and obviously needed in general) -- # do this last to leave the matrixmode standard # (status of bugs & fixes unclear -- hard to test since even Highlightable(projection=True) w/o any change to # projection matrix (test _9cx) doesn't work!) offset = (-delegate.bright, delegate.bbottom) # only correct for LOWER_RIGHT glTranslatef(offset[0], offset[1], 0) self.drawkid( delegate) ## delegate.draw() finally: glMatrixMode(GL_PROJECTION) glPopMatrix() glMatrixMode(GL_MODELVIEW) # be sure to do this last, to leave the matrixmode standard glPopMatrix() return
def glRenderMode( newMode ): """Change to the given rendering mode If the current mode is GL_FEEDBACK or GL_SELECT, return the current buffer appropriate to the mode """ # must get the current mode to determine operation... from OpenGL.GL import glGetIntegerv from OpenGL.GL import selection, feedback currentMode = glGetIntegerv( simple.GL_RENDER_MODE ) try: currentMode = currentMode[0] except (TypeError,ValueError,IndexError) as err: pass if currentMode in (simple.GL_RENDER,0): # no array needs to be returned... return simple.glRenderMode( newMode ) result = simple.glRenderMode( newMode ) # result is now an integer telling us how many elements were copied... if result < 0: if currentMode == simple.GL_SELECT: raise error.GLError( simple.GL_STACK_OVERFLOW, "glSelectBuffer too small to hold selection results", ) elif currentMode == simple.GL_FEEDBACK: raise error.GLError( simple.GL_STACK_OVERFLOW, "glFeedbackBuffer too small to hold selection results", ) else: raise error.GLError( simple.GL_STACK_OVERFLOW, "Unknown glRenderMode buffer (%s) too small to hold selection results"%( currentMode, ), ) # Okay, now that the easy cases are out of the way... # Do we have a pre-stored pointer about which the user already knows? context = platform.GetCurrentContext() if context == 0: raise error.Error( """Returning from glRenderMode without a valid context!""" ) arrayConstant, wrapperFunction = { simple.GL_FEEDBACK: (simple.GL_FEEDBACK_BUFFER_POINTER,feedback.parseFeedback), simple.GL_SELECT: (simple.GL_SELECTION_BUFFER_POINTER, selection.GLSelectRecord.fromArray), }[ currentMode ] current = contextdata.getValue( arrayConstant ) # XXX check to see if it's the *same* array we set currently! if current is None: current = glGetPointerv( arrayConstant ) # XXX now, can turn the array into the appropriate wrapper type... if wrapperFunction: current = wrapperFunction( current, result ) return current
def glRenderMode(newMode): """Change to the given rendering mode If the current mode is GL_FEEDBACK or GL_SELECT, return the current buffer appropriate to the mode """ # must get the current mode to determine operation... from OpenGL.GL import glGetIntegerv from OpenGL.GL import selection, feedback currentMode = glGetIntegerv(simple.GL_RENDER_MODE) try: currentMode = currentMode[0] except (TypeError, ValueError, IndexError) as err: pass if currentMode in (simple.GL_RENDER, 0): # no array needs to be returned... return simple.glRenderMode(newMode) result = simple.glRenderMode(newMode) # result is now an integer telling us how many elements were copied... if result < 0: if currentMode == simple.GL_SELECT: raise error.GLError( simple.GL_STACK_OVERFLOW, "glSelectBuffer too small to hold selection results", ) elif currentMode == simple.GL_FEEDBACK: raise error.GLError( simple.GL_STACK_OVERFLOW, "glFeedbackBuffer too small to hold selection results", ) else: raise error.GLError( simple.GL_STACK_OVERFLOW, "Unknown glRenderMode buffer (%s) too small to hold selection results" % (currentMode, ), ) # Okay, now that the easy cases are out of the way... # Do we have a pre-stored pointer about which the user already knows? context = platform.GetCurrentContext() if context == 0: raise error.Error( """Returning from glRenderMode without a valid context!""") arrayConstant, wrapperFunction = { simple.GL_FEEDBACK: (simple.GL_FEEDBACK_BUFFER_POINTER, feedback.parseFeedback), simple.GL_SELECT: (simple.GL_SELECTION_BUFFER_POINTER, selection.GLSelectRecord.fromArray), }[currentMode] current = contextdata.getValue(arrayConstant) # XXX check to see if it's the *same* array we set currently! if current is None: current = glGetPointerv(arrayConstant) # XXX now, can turn the array into the appropriate wrapper type... if wrapperFunction: current = wrapperFunction(current, result) return current
def draw(self, camera_matrix: numpy.ndarray): """Draw all of the levels.""" for level, transform, is_mirrored in zip( self._objects, self._transformation_matrices, self._is_mirrored ): cull_state = glGetIntegerv(GL_CULL_FACE_MODE) if is_mirrored: glCullFace(GL_FRONT) else: glCullFace(GL_BACK) level.draw(numpy.matmul(camera_matrix, transform)) glCullFace(cull_state)
def glRenderMode( newMode ): """Change to the given rendering mode If the current mode is GL_FEEDBACK or GL_SELECT, return the current buffer appropriate to the mode """ # must get the current mode to determine operation... from OpenGL.GL import glGetIntegerv from OpenGL.GL import selection, feedback currentMode = glGetIntegerv( simple.GL_RENDER_MODE ) try: currentMode = currentMode[0] except (TypeError,ValueError,IndexError), err: pass
def hasGLExtension( specifier ): """Given a string specifier, check for extension being available""" global AVAILABLE_GL_EXTENSIONS specifier = as_8_bit(specifier).replace(as_8_bit('.'),as_8_bit('_')) if specifier.startswith( VERSION_PREFIX ): specifier = [ int(x) for x in specifier[ len(VERSION_PREFIX):].split(as_8_bit('_')) ] version = getGLVersion() if not version: return version return specifier <= version else: from OpenGL.GL import glGetString, GL_EXTENSIONS from OpenGL import error if not AVAILABLE_GL_EXTENSIONS: try: AVAILABLE_GL_EXTENSIONS[:] = glGetString( GL_EXTENSIONS ).split() except (AttributeError, error.GLError) as err: # OpenGL 3.0 deprecates glGetString( GL_EXTENSIONS ) from OpenGL.GL.VERSION.GL_3_0 import GL_NUM_EXTENSIONS, glGetStringi from OpenGL.GL import glGetIntegerv count = glGetIntegerv( GL_NUM_EXTENSIONS ) for i in range( count ): extension = glGetStringi( GL_EXTENSIONS, i ) AVAILABLE_GL_EXTENSIONS.append( extension ) # Add included-by-reference extensions... version = getGLVersion() if not version: # should not be possible? return version check = tuple( version[:2] ) for (v,v_exts) in VERSION_EXTENSIONS: if v <= check: for v_ext in v_exts: if v_ext not in AVAILABLE_GL_EXTENSIONS: AVAILABLE_GL_EXTENSIONS.append( v_ext ) else: break result = specifier in AVAILABLE_GL_EXTENSIONS log.info( 'GL Extension %s %s', specifier, ['unavailable','available'][bool(result)] ) return result
def render_scene(self, object_list, view_matrix, projection_matrix, orthographic_matrix, graphics_context): projection_matrix = np.array(projection_matrix, np.double) viewport = glGetIntegerv(GL_VIEWPORT) orthographic_matrix = np.array(orthographic_matrix, np.double) view_matrix = np.array(view_matrix, np.double) for o in object_list: if "animation_controller" in o._components: c = o._components["animation_controller"] if hasattr(c, "get_labeled_points"): labels, points = c.get_labeled_points() for i, l in enumerate(labels): pos = points[i] wx, wy, _ = gluProject(pos[0], pos[1], pos[2], view_matrix, projection_matrix, viewport) wy = graphics_context.height - wy self.draw(orthographic_matrix, (wx, wy), l[:self.max_label_length])
def _getPickingRay(self, x, y): ''' Process the ray for the current mouse position ''' viewport = glGetIntegerv(GL_VIEWPORT) model_mat = np.array(glGetDoublev(GL_MODELVIEW_MATRIX)) proj_mat = np.array(glGetDoublev(GL_PROJECTION_MATRIX)) # win_coord = (x*2, viewport[3] - y*2) win_coord = (x, viewport[3] - y) near_point = np.array( GLU.gluUnProject(win_coord[0], win_coord[1], 0.0, model_mat, proj_mat, viewport)) far_point = np.array( GLU.gluUnProject(win_coord[0], win_coord[1], 1.0, model_mat, proj_mat, viewport)) return far_point - near_point
def _setup_projection( self, glselect=False ): #bruce 050608 split this out; 050615 revised docstring """ Set up standard projection matrix contents using aspect, vdist, and some attributes of self. @warning: leaves matrixmode as GL_PROJECTION. Optional arg glselect should be False (default) or a 4-tuple (to prepare for GL_SELECT picking). """ glMatrixMode(GL_PROJECTION) glLoadIdentity() scale = self.scale #bruce 050608 used this to clarify following code near, far = self.near, self.far #bruce 080219 moved these from one of two callers into here, # to fix bug when insert from partlib is first operation in NE1 self.aspect = (self.width + 0.0) / (self.height + 0.0) self.vdist = 6.0 * scale if glselect: x, y, w, h = glselect gluPickMatrix( x, y, w, h, glGetIntegerv( GL_VIEWPORT ) #k is this arg needed? it might be the default... ) if self.ortho: glOrtho(-scale * self.aspect, scale * self.aspect, -scale, scale, self.vdist * near, self.vdist * far) else: glFrustum(-scale * near * self.aspect, scale * near * self.aspect, -scale * near, scale * near, self.vdist * near, self.vdist * far) return
def draw(self, camera_matrix: numpy.ndarray, camera_position: PointCoordinatesAny = None): """ Draw the selection box :param camera_matrix: 4x4 transformation matrix for the camera :param camera_position: The position of the camera. Used to flip draw direction if camera inside box. :return: """ self._setup() if self._needs_rebuild: self._create_geometry() transformation_matrix = numpy.matmul(camera_matrix, self.transformation_matrix) depth_state = glGetBooleanv(GL_DEPTH_TEST) cull_state = glGetIntegerv(GL_CULL_FACE_MODE) # draw the lines around the boxes self.draw_start = 0 self.draw_count = 36 if depth_state: glDisable(GL_DEPTH_TEST) self._draw_mode = GL_LINE_STRIP super()._draw(transformation_matrix) if depth_state: glEnable(GL_DEPTH_TEST) if camera_position is not None: if camera_position in self: glCullFace(GL_FRONT) else: glCullFace(GL_BACK) self._draw_mode = GL_TRIANGLES self.draw_start = 36 # 6 faces, 9 quads/face, 2 triangles/quad, 3 verts/triangle self.draw_count = 324 super()._draw(transformation_matrix) glCullFace(cull_state)
def draw(self, camera_matrix: numpy.ndarray, camera_position: PointCoordinatesAny = None): """ Draw the selection box :param camera_matrix: 4x4 transformation matrix for the camera :param camera_position: The position of the camera. Used to flip draw direction if camera inside box. :return: """ self._setup() if self._needs_rebuild: self._create_geometry() self._draw_mode = GL_TRIANGLES transformation_matrix = numpy.matmul(camera_matrix, self.transformation_matrix) glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT | GL_ENABLE_BIT) # store opengl state if camera_position is not None and camera_position in self: mode = glGetIntegerv(GL_CULL_FACE_MODE) if mode == GL_BACK: glCullFace(GL_FRONT) elif mode == GL_FRONT: glCullFace(GL_BACK) self.draw_start = 0 self.draw_count = 36 super()._draw(transformation_matrix) # draw the lines around the boxes glDisable(GL_DEPTH_TEST) self._draw_mode = GL_LINE_STRIP super()._draw(transformation_matrix) glPopAttrib() # reset to starting state
def _setup_projection(self, glselect=False): # bruce 050608 split this out; 050615 revised docstring """ Set up standard projection matrix contents using aspect, vdist, and some attributes of self. @warning: leaves matrixmode as GL_PROJECTION. Optional arg glselect should be False (default) or a 4-tuple (to prepare for GL_SELECT picking). """ glMatrixMode(GL_PROJECTION) glLoadIdentity() scale = self.scale # bruce 050608 used this to clarify following code near, far = self.near, self.far # bruce 080219 moved these from one of two callers into here, # to fix bug when insert from partlib is first operation in NE1 self.aspect = (self.width + 0.0) / (self.height + 0.0) self.vdist = 6.0 * scale if glselect: x, y, w, h = glselect gluPickMatrix(x, y, w, h, glGetIntegerv(GL_VIEWPORT)) # k is this arg needed? it might be the default... if self.ortho: glOrtho(-scale * self.aspect, scale * self.aspect, -scale, scale, self.vdist * near, self.vdist * far) else: glFrustum( -scale * near * self.aspect, scale * near * self.aspect, -scale * near, scale * near, self.vdist * near, self.vdist * far, ) return
def _setup_projection(self, glselect = False): ### WARNING: This is not actually private! TODO: rename it. """ Set up standard projection matrix contents using various attributes of self (aspect, vdist, scale, zoomFactor). Also reads the current OpenGL viewport bounds in window coordinates. (Warning: leaves matrixmode as GL_PROJECTION.) @param glselect: False (default) normally, or a 4-tuple (format not documented here) to prepare for GL_SELECT picking by calling gluPickMatrix(). If you are really going to draw in the pick window (for GL_RENDER and glReadPixels picking, rather than GL_SELECT picking), don't forget to set the glViewport *after* calling _setup_projection. Here's why: gluPickMatrix needs to know the current *whole-window* viewport, in order to set up a projection matrix to map a small portion of it to the clipping boundaries for GL_SELECT. From the gluPickMatrix doc page: viewport: Specifies the current viewport (as from a glGetIntegerv call). Description: gluPickMatrix creates a projection matrix that can be used to restrict drawing to a small region of the viewport. In the graphics pipeline, the clipper actually works in homogeneous coordinates, clipping polygons to the {X,Y}==+-W boundaries. This saves the work of doing the homogeneous division: {X,Y}/W==+-1.0, (and avoiding problems when W is zero for points on the eye plane in Z,) but it comes down to the same thing as clipping to X,Y==+-1 in orthographic. So the projection matrix decides what 3D model-space planes map to +-1 in X,Y. (I think it maps [near,far] to [0,1] in Z, because they're not clipped symmetrically.) Then glViewport sets the hardware transform that determines where the +-1 square of clipped output goes in screen pixels within the window. Normally you don't actually draw pixels while picking in GL_SELECT mode because the pipeline outputs hits after the clipping stage, so gluPickMatrix only reads the viewport and sets the projection matrix. """ #bruce 080912 moved this from GLPane into GLPane_minimal glMatrixMode(GL_PROJECTION) glLoadIdentity() scale = self.scale * self.zoomFactor near, far = self.near, self.far aspect = self.aspect vdist = self.vdist if glselect: x, y, w, h = glselect gluPickMatrix( x, y, w, h, glGetIntegerv( GL_VIEWPORT ) #k is this arg needed? it might be the default... ) if self.ortho: glOrtho( - scale * aspect, scale * aspect, - scale, scale, vdist * near, vdist * far ) else: glFrustum( - scale * near * aspect, scale * near * aspect, - scale * near, scale * near, vdist * near, vdist * far) return
def do_glselect_if_wanted(self): # bruce 070919 split this out """ Do the glRenderMode(GL_SELECT) drawing, and/or the glname-color drawing for shader primitives, used to guess which object might be under the mouse, for one drawing frame, if desired for this frame. Report results by storing candidate mouseover objects in self.glselect_dict. The depth buffer is initially clear, and must be clear when we're done as well. @note: does not do related individual object depth/stencil buffer drawing -- caller must do that on some or all of the objects we store into self.glselect_dict. """ if self.glselect_wanted: # note: this will be reset below. ###@@@ WARNING: The original code for this, here in GLPane, has been duplicated and slightly modified # in at least three other places (search for glRenderMode to find them). This is bad; common code # should be used. Furthermore, I suspect it's sometimes needlessly called more than once per frame; # that should be fixed too. [bruce 060721 comment] wX, wY, self.targetdepth = self.glselect_wanted # wX, wY is the point to do the hit-test at # targetdepth is the depth buffer value to look for at that point, during ordinary drawing phase # (could also be used to set up clipping planes to further restrict hit-test, but this isn't yet done) # (Warning: targetdepth could in theory be out of date, if more events come between bareMotion # and the one caused by its gl_update, whose paintGL is what's running now, and if those events # move what's drawn. Maybe that could happen with mousewheel events or (someday) with keypresses # having a graphical effect. Ideally we'd count intentional redraws, and disable this picking in that case.) self.wX, self.wY = wX, wY self.glselect_wanted = 0 pwSize = 1 # Pick window size. Russ 081128: Was 3. # Bruce: Replace 3, 3 with 1, 1? 5, 5? not sure whether this will # matter... in principle should have no effect except speed. # Russ: For glname rendering, 1x1 is better because it doesn't # have window boundary issues. We get the coords of a single # pixel in the window for the mouse position. # bruce 050615 for use by nodes which want to set up their own projection matrix. self.current_glselect = (wX, wY, pwSize, pwSize) self._setup_projection(glselect=self.current_glselect) # option makes it use gluPickMatrix # Russ 081209: Added. debugPicking = debug_pref("GLPane: debug mouseover picking?", Choice_boolean_False, prefs_key=True) if self.enabled_shaders(): # TODO: optimization: find an appropriate place to call # _compute_frustum_planes. [bruce 090105 comment] # Russ 081122: There seems to be no way to access the GL name # stack in shaders. Instead, for mouseover, draw shader # primitives with glnames as colors in glRenderMode(GL_RENDER), # then read back the pixel color (glname) and depth value. # Temporarily replace the full-size viewport with a little one # at the mouse location, matching the pick matrix location. # Otherwise, we will draw a closeup of that area into the whole # window, rather than a few pixels. (This wasn't needed when we # only used GL_SELECT rendering mode here, because that doesn't # modify the frame buffer -- it just returns hits by graphics # primitives when they are inside the clipping boundaries.) # # (Don't set the viewport *before* _setup_projection(), since # that method needs to read the current whole-window viewport # to set up glselect. See explanation in its docstring.) savedViewport = glGetIntegerv(GL_VIEWPORT) glViewport(wX, wY, pwSize, pwSize) # Same as current_glselect. # First, clear the pixel RGBA to zeros and a depth of 1.0 (far), # so we won't confuse a color with a glname if there are # no shader primitives drawn over this pixel. saveDepthFunc = glGetInteger(GL_DEPTH_FUNC) glDepthFunc(GL_ALWAYS) glWindowPos3i(wX, wY, 1) # Note the Z coord. gl_format, gl_type = GL_RGBA, GL_UNSIGNED_BYTE glDrawPixels(pwSize, pwSize, gl_format, gl_type, (0, 0, 0, 0)) glDepthFunc(saveDepthFunc) # needed, though we'll change it again # We must be in glRenderMode(GL_RENDER) (as usual) when this is called. # Note: _setup_projection leaves the matrix mode as GL_PROJECTION. glMatrixMode(GL_MODELVIEW) shaders = self.enabled_shaders() try: # Set flags so that we will use glnames-as-color mode # in shaders, and draw only shader primitives. # (Ideally we would also draw all non-shader primitives # as some other color, unequal to all glname colors # (or derived from a fake glname for that purpose), # in order to obscure shader primitives where appropriate. # This is intended to be done but is not yet implemented. # [bruce 090105 addendum]) for shader in shaders: shader.setPicking(True) self.set_drawing_phase("glselect_glname_color") for stereo_image in self.stereo_images_to_draw: self._enable_stereo(stereo_image) try: self._do_graphicsMode_Draw(for_mouseover_highlighting=True) # note: we can't disable depth writing here, # since we need it to make sure the correct # shader object comes out on top, or is # obscured by a DL object. Instead, we'll # clear the depth buffer again (at this pixel) # below. [bruce 090105] finally: self._disable_stereo() except: print_compact_traceback( "exception in or around _do_graphicsMode_Draw() during glname_color;" "drawing ignored; restoring modelview matrix: " ) # REVIEW: what does "drawing ignored" mean, in that message? [bruce 090105 question] glMatrixMode(GL_MODELVIEW) self._setup_modelview() ### REVIEW: correctness of this is unreviewed! # now it's important to continue, at least enough to restore other gl state pass for shader in shaders: shader.setPicking(False) self.set_drawing_phase("?") # Restore the viewport. glViewport(savedViewport[0], savedViewport[1], savedViewport[2], savedViewport[3]) # Read pixel value from the back buffer and re-assemble glname. glFinish() # Make sure the drawing has completed. # REVIEW: is this glFinish needed? [bruce 090105 comment] rgba = glReadPixels(wX, wY, 1, 1, gl_format, gl_type)[0][0] pixZ = glReadPixelsf(wX, wY, 1, 1, GL_DEPTH_COMPONENT)[0][0] # Clear our depth pixel to 1.0 (far), so we won't mess up the # subsequent call of preDraw_glselect_dict. # (The following is not the most direct way, but it ought to work. # Note that we also clear the color pixel, since (being a glname) # it has no purpose remaining in the color buffer -- either it's # changed later, or, if not, that's a bug, but we'd rather have # it black than a random color.) [bruce 090105 bugfix] glDepthFunc(GL_ALWAYS) glWindowPos3i(wX, wY, 1) # Note the Z coord. glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) gl_format, gl_type = GL_RGBA, GL_UNSIGNED_BYTE glDrawPixels(pwSize, pwSize, gl_format, gl_type, (0, 0, 0, 0)) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) glDepthFunc(saveDepthFunc) # Comes back sign-wrapped, in spite of specifying UNSIGNED_BYTE. def us(b): if b < 0: return 256 + b return b bytes = tuple([us(b) for b in rgba]) ##glname = (bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3]) ## Temp fix: Ignore the last byte, which always comes back 255 on Windows. glname = bytes[0] << 16 | bytes[1] << 8 | bytes[2] if debugPicking: print ( "shader mouseover xy %d %d, " % (wX, wY) + "rgba bytes (0x%x, 0x%x, 0x%x, 0x%x), " % bytes + "Z %f, glname 0x%x" % (pixZ, glname) ) pass ### XXX This ought to be better-merged with the DL selection below. if glname: obj = self.object_for_glselect_name(glname) if debugPicking: print "shader mouseover glname=%r, obj=%r." % (glname, obj) if obj is None: # REVIEW: does this happen for mouse over a non-shader primitive? # [bruce 090105 question] #### Note: this bug is common. Guess: we are still drawing # ordinary colors for some primitives and/or for the # background, and they are showing up here and confusing # us. To help debug this, print the color too. But testing # shows it's not so simple -- e.g. for rung bonds it happens # where shader sphere and cylinder overlap, but not on either # one alone; for strand bonds it also happens on the bonds alone # (tested in Build DNA, in or not in Insert DNA). # [bruce 090218] # # Update: Since it's so common, I need to turn it off by default. # Q: is the situation safe? # A: if a color looks like a real glname by accident, # we'll get some random candidate object -- perhaps a killed one # or from a different Part or even a closed assy -- # and try to draw it. That doesn't sound very safe. Unfortunately # there is no perfect way to filter selobjs for safety, in the # current still-informal Selobj_API. The best approximation is # selobj_still_ok, and it should always say yes for the usual kinds, # so I'll add it as a check in the 'else' clause below. # [bruce 090311] if debug_flags.atom_debug: print "bug: object_for_glselect_name returns None for glname %r (color %r)" % ( glname, bytes, ) else: if self.graphicsMode.selobj_still_ok(obj): # bruce 090311 added condition, explained above self.glselect_dict[id(obj)] = obj else: # This should be rare but possible. Leave it on briefly and see # if it's ever common. If possible, gate it by atom_debug before # the release. [bruce 090311] print "fyi: glname-color selobj %r rejected since not selobj_still_ok" % obj pass pass pass if self._use_frustum_culling: self._compute_frustum_planes() # piotr 080331 - the frustum planes have to be setup after the # projection matrix is setup. I'm not sure if there may # be any side effects - see the comment below about # possible optimization. glSelectBuffer(self.SIZE_FOR_glSelectBuffer) # Note: this allocates a new select buffer, # and glRenderMode(GL_RENDER) returns it and forgets it, # so it's required before *each* call of glRenderMode(GL_SELECT) + # glRenderMode(GL_RENDER), not just once to set the size. # Ref: http://pyopengl.sourceforge.net/documentation/opengl_diffs.html # [bruce 080923 comment] glInitNames() # REVIEW: should we also set up a clipping plane just behind the # hit point, as (I think) is done in ThumbView, to reduce the # number of candidate objects? This might be a significant # optimization, though I don't think it eliminates the chance # of having multiple candidates. [bruce 080917 comment] glRenderMode(GL_SELECT) glMatrixMode(GL_MODELVIEW) try: self.set_drawing_phase("glselect") # bruce 070124 for stereo_image in self.stereo_images_to_draw: self._enable_stereo(stereo_image) try: self._do_graphicsMode_Draw(for_mouseover_highlighting=True) finally: self._disable_stereo() except: print_compact_traceback( "exception in or around _do_graphicsMode_Draw() during GL_SELECT; " "ignored; restoring modelview matrix: " ) glMatrixMode(GL_MODELVIEW) self._setup_modelview() ### REVIEW: correctness of this is unreviewed! # now it's important to continue, at least enough to restore other gl state self._frustum_planes_available = False # piotr 080331 # just to be safe and not use the frustum planes computed for # the pick matrix self.set_drawing_phase("?") self.current_glselect = False # REVIEW: On systems with no stencil buffer, I think we'd also need # to draw selobj here in highlighted form (in case that form is # bigger than when it's not highlighted), or (easier & faster) # just always pretend it passes the hit test and add it to # glselect_dict -- and, make sure to give it "first dibs" for being # the next selobj. I'll implement some of this now (untested when # no stencil buffer) but not yet all. [bruce 050612] selobj = self.selobj if selobj is not None: self.glselect_dict[id(selobj)] = selobj # (review: is the following note correct?) # note: unneeded, if the func that looks at this dict always # tries selobj first (except for a kluge near # "if self.glselect_dict", commented on below) glFlush() hit_records = list(glRenderMode(GL_RENDER)) if debugPicking: print "DLs %d hits" % len(hit_records) for (near, far, names) in hit_records: # see example code, renderpass.py ## print "hit record: near, far, names:", near, far, names # e.g. hit record: near, far, names: 1439181696 1453030144 (1638426L,) # which proves that near/far are too far apart to give actual depth, # in spite of the 1- or 3-pixel drawing window (presumably they're vertices # taken from unclipped primitives, not clipped ones). del near, far if 1: # partial workaround for bug 1527. This can be removed once that bug (in drawer.py) # is properly fixed. This exists in two places -- GLPane.py and modes.py. [bruce 060217] if names and names[-1] == 0: print "%d(g) partial workaround for bug 1527: removing 0 from end of namestack:" % env.redraw_counter, names names = names[:-1] if names: # For now, we only use the last element of names, # though (as of long before 080917) it is often longer: # - some code pushes the same name twice (directly and # via ColorSorter) (see 060725 debug print below); # - chunks push a name even when they draw atoms/bonds # which push their own names (see 080411 comment below). # # Someday: if we ever support "name/subname paths" we'll # probably let first name interpret the remaining ones. # In fact, if nodes change projection or viewport for # their kids, and/or share their kids, they'd need to # push their own names on the stack, so we'd know how # to redraw the kids, or which ones are meant when they # are shared. if debug_flags.atom_debug and len(names) > 1: # bruce 060725 if len(names) == 2 and names[0] == names[1]: if not env.seen_before("dual-names bug"): # this happens for Atoms (colorsorter bug??) print "debug (once-per-session message): why are some glnames duplicated on the namestack?", names else: # Note: as of sometime before 080411, this became common -- # I guess that chunks (which recently acquired glselect names) # are pushing their names even while drawing their atoms and bonds. # I am not sure if this can cause any problems -- almost surely not # directly, but maybe the nestedness of highlighted appearances could # violate some assumptions made by the highlight code... anyway, # to reduce verbosity I need to not print this when the deeper name # is that of a chunk, and there are exactly two names. [bruce 080411] if len(names) == 2 and isinstance(self.object_for_glselect_name(names[0]), self.assy.Chunk): if not env.seen_before("nested names for Chunk"): print "debug (once-per-session message): nested glnames for a Chunk: ", names else: print "debug fyi: len(names) == %d (names = %r)" % (len(names), names) obj = self.object_for_glselect_name(names[-1]) # k should always return an obj if obj is None: print "bug: object_for_glselect_name returns None for name %r at end of namestack %r" % ( names[-1], names, ) else: self.glselect_dict[id(obj)] = obj # note: outside of this method, one of these will be # chosen to be saved as self.selobj and rerendered # in "highlighted" form ##if 0: ## # this debug print was useful for debugging bug 2945, ## # and when it happens it's usually a bug, ## # but not always: ## # - it's predicted to happen for ChunkDrawer._renderOverlayText ## # - and whenever we're using a whole-chunk display style ## # so we can't leave it in permanently. [bruce 081211] ## if isinstance( obj, self.assy.Chunk ): ## print "\n*** namestack topped with a chunk:", obj pass continue # next hit_record # e maybe we should now sort glselect_dict by "hit priority" # (for depth-tiebreaking), or at least put selobj first. # (or this could be done lower down, where it's used.) # [I think we do this now...] return # from do_glselect_if_wanted
'<table border="1" cellspacing="0" cellpadding="2"><thead><tr><h2>%s</h2></tr></thead>' % desc) for i in info[module_name]: try: if len(i) == 2: key, value = i else: key, id, t = i if t[0] == 'b': value = glGetBooleanv(id) if operator.isSequence(value): value = map(_boolean, value) else: value = _boolean(value) elif t[0] == 'i': value = glGetIntegerv(id) elif t[0] == 'd': value = glGetDoublev(id) elif t[0] == 'e': if len(t) > 1: if t[1] == 'u': x = gluGetString(id) else: x = glGetString(id) else: x = id x = string.split(x) x.sort() y = [] for ext in x: try:
def do_glselect_if_wanted(self): #bruce 070919 split this out """ Do the glRenderMode(GL_SELECT) drawing, and/or the glname-color drawing for shader primitives, used to guess which object might be under the mouse, for one drawing frame, if desired for this frame. Report results by storing candidate mouseover objects in self.glselect_dict. The depth buffer is initially clear, and must be clear when we're done as well. @note: does not do related individual object depth/stencil buffer drawing -- caller must do that on some or all of the objects we store into self.glselect_dict. """ if self.glselect_wanted: # note: this will be reset below. ###@@@ WARNING: The original code for this, here in GLPane, has been duplicated and slightly modified # in at least three other places (search for glRenderMode to find them). This is bad; common code # should be used. Furthermore, I suspect it's sometimes needlessly called more than once per frame; # that should be fixed too. [bruce 060721 comment] wX, wY, self.targetdepth = self.glselect_wanted # wX, wY is the point to do the hit-test at # targetdepth is the depth buffer value to look for at that point, during ordinary drawing phase # (could also be used to set up clipping planes to further restrict hit-test, but this isn't yet done) # (Warning: targetdepth could in theory be out of date, if more events come between bareMotion # and the one caused by its gl_update, whose paintGL is what's running now, and if those events # move what's drawn. Maybe that could happen with mousewheel events or (someday) with keypresses # having a graphical effect. Ideally we'd count intentional redraws, and disable this picking in that case.) self.wX, self.wY = wX, wY self.glselect_wanted = 0 pwSize = 1 # Pick window size. Russ 081128: Was 3. # Bruce: Replace 3, 3 with 1, 1? 5, 5? not sure whether this will # matter... in principle should have no effect except speed. # Russ: For glname rendering, 1x1 is better because it doesn't # have window boundary issues. We get the coords of a single # pixel in the window for the mouse position. #bruce 050615 for use by nodes which want to set up their own projection matrix. self.current_glselect = (wX, wY, pwSize, pwSize) self._setup_projection(glselect=self.current_glselect ) # option makes it use gluPickMatrix # Russ 081209: Added. debugPicking = debug_pref("GLPane: debug mouseover picking?", Choice_boolean_False, prefs_key=True) if self.enabled_shaders(): # TODO: optimization: find an appropriate place to call # _compute_frustum_planes. [bruce 090105 comment] # Russ 081122: There seems to be no way to access the GL name # stack in shaders. Instead, for mouseover, draw shader # primitives with glnames as colors in glRenderMode(GL_RENDER), # then read back the pixel color (glname) and depth value. # Temporarily replace the full-size viewport with a little one # at the mouse location, matching the pick matrix location. # Otherwise, we will draw a closeup of that area into the whole # window, rather than a few pixels. (This wasn't needed when we # only used GL_SELECT rendering mode here, because that doesn't # modify the frame buffer -- it just returns hits by graphics # primitives when they are inside the clipping boundaries.) # # (Don't set the viewport *before* _setup_projection(), since # that method needs to read the current whole-window viewport # to set up glselect. See explanation in its docstring.) savedViewport = glGetIntegerv(GL_VIEWPORT) glViewport(wX, wY, pwSize, pwSize) # Same as current_glselect. # First, clear the pixel RGBA to zeros and a depth of 1.0 (far), # so we won't confuse a color with a glname if there are # no shader primitives drawn over this pixel. saveDepthFunc = glGetInteger(GL_DEPTH_FUNC) glDepthFunc(GL_ALWAYS) glWindowPos3i(wX, wY, 1) # Note the Z coord. gl_format, gl_type = GL_RGBA, GL_UNSIGNED_BYTE glDrawPixels(pwSize, pwSize, gl_format, gl_type, (0, 0, 0, 0)) glDepthFunc( saveDepthFunc) # needed, though we'll change it again # We must be in glRenderMode(GL_RENDER) (as usual) when this is called. # Note: _setup_projection leaves the matrix mode as GL_PROJECTION. glMatrixMode(GL_MODELVIEW) shaders = self.enabled_shaders() try: # Set flags so that we will use glnames-as-color mode # in shaders, and draw only shader primitives. # (Ideally we would also draw all non-shader primitives # as some other color, unequal to all glname colors # (or derived from a fake glname for that purpose), # in order to obscure shader primitives where appropriate. # This is intended to be done but is not yet implemented. # [bruce 090105 addendum]) for shader in shaders: shader.setPicking(True) self.set_drawing_phase("glselect_glname_color") for stereo_image in self.stereo_images_to_draw: self._enable_stereo(stereo_image) try: self._do_graphicsMode_Draw( for_mouseover_highlighting=True) # note: we can't disable depth writing here, # since we need it to make sure the correct # shader object comes out on top, or is # obscured by a DL object. Instead, we'll # clear the depth buffer again (at this pixel) # below. [bruce 090105] finally: self._disable_stereo() except: print_compact_traceback( "exception in or around _do_graphicsMode_Draw() during glname_color;" "drawing ignored; restoring modelview matrix: ") # REVIEW: what does "drawing ignored" mean, in that message? [bruce 090105 question] glMatrixMode(GL_MODELVIEW) self._setup_modelview( ) ### REVIEW: correctness of this is unreviewed! # now it's important to continue, at least enough to restore other gl state pass for shader in shaders: shader.setPicking(False) self.set_drawing_phase('?') # Restore the viewport. glViewport(savedViewport[0], savedViewport[1], savedViewport[2], savedViewport[3]) # Read pixel value from the back buffer and re-assemble glname. glFinish() # Make sure the drawing has completed. # REVIEW: is this glFinish needed? [bruce 090105 comment] rgba = glReadPixels(wX, wY, 1, 1, gl_format, gl_type)[0][0] pixZ = glReadPixelsf(wX, wY, 1, 1, GL_DEPTH_COMPONENT)[0][0] # Clear our depth pixel to 1.0 (far), so we won't mess up the # subsequent call of preDraw_glselect_dict. # (The following is not the most direct way, but it ought to work. # Note that we also clear the color pixel, since (being a glname) # it has no purpose remaining in the color buffer -- either it's # changed later, or, if not, that's a bug, but we'd rather have # it black than a random color.) [bruce 090105 bugfix] glDepthFunc(GL_ALWAYS) glWindowPos3i(wX, wY, 1) # Note the Z coord. glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) gl_format, gl_type = GL_RGBA, GL_UNSIGNED_BYTE glDrawPixels(pwSize, pwSize, gl_format, gl_type, (0, 0, 0, 0)) glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) glDepthFunc(saveDepthFunc) # Comes back sign-wrapped, in spite of specifying UNSIGNED_BYTE. def us(b): if b < 0: return 256 + b return b bytes = tuple([us(b) for b in rgba]) ##glname = (bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3]) ## Temp fix: Ignore the last byte, which always comes back 255 on Windows. glname = (bytes[0] << 16 | bytes[1] << 8 | bytes[2]) if debugPicking: print("shader mouseover xy %d %d, " % (wX, wY) + "rgba bytes (0x%x, 0x%x, 0x%x, 0x%x), " % bytes + "Z %f, glname 0x%x" % (pixZ, glname)) pass ### XXX This ought to be better-merged with the DL selection below. if glname: obj = self.object_for_glselect_name(glname) if debugPicking: print "shader mouseover glname=%r, obj=%r." % (glname, obj) if obj is None: # REVIEW: does this happen for mouse over a non-shader primitive? # [bruce 090105 question] #### Note: this bug is common. Guess: we are still drawing # ordinary colors for some primitives and/or for the # background, and they are showing up here and confusing # us. To help debug this, print the color too. But testing # shows it's not so simple -- e.g. for rung bonds it happens # where shader sphere and cylinder overlap, but not on either # one alone; for strand bonds it also happens on the bonds alone # (tested in Build DNA, in or not in Insert DNA). # [bruce 090218] # # Update: Since it's so common, I need to turn it off by default. # Q: is the situation safe? # A: if a color looks like a real glname by accident, # we'll get some random candidate object -- perhaps a killed one # or from a different Part or even a closed assy -- # and try to draw it. That doesn't sound very safe. Unfortunately # there is no perfect way to filter selobjs for safety, in the # current still-informal Selobj_API. The best approximation is # selobj_still_ok, and it should always say yes for the usual kinds, # so I'll add it as a check in the 'else' clause below. # [bruce 090311] if debug_flags.atom_debug: print "bug: object_for_glselect_name returns None for glname %r (color %r)" % ( glname, bytes) else: if self.graphicsMode.selobj_still_ok(obj): #bruce 090311 added condition, explained above self.glselect_dict[id(obj)] = obj else: # This should be rare but possible. Leave it on briefly and see # if it's ever common. If possible, gate it by atom_debug before # the release. [bruce 090311] print "fyi: glname-color selobj %r rejected since not selobj_still_ok" % obj pass pass pass if self._use_frustum_culling: self._compute_frustum_planes() # piotr 080331 - the frustum planes have to be setup after the # projection matrix is setup. I'm not sure if there may # be any side effects - see the comment below about # possible optimization. glSelectBuffer(self.SIZE_FOR_glSelectBuffer) # Note: this allocates a new select buffer, # and glRenderMode(GL_RENDER) returns it and forgets it, # so it's required before *each* call of glRenderMode(GL_SELECT) + # glRenderMode(GL_RENDER), not just once to set the size. # Ref: http://pyopengl.sourceforge.net/documentation/opengl_diffs.html # [bruce 080923 comment] glInitNames() # REVIEW: should we also set up a clipping plane just behind the # hit point, as (I think) is done in ThumbView, to reduce the # number of candidate objects? This might be a significant # optimization, though I don't think it eliminates the chance # of having multiple candidates. [bruce 080917 comment] glRenderMode(GL_SELECT) glMatrixMode(GL_MODELVIEW) try: self.set_drawing_phase('glselect') #bruce 070124 for stereo_image in self.stereo_images_to_draw: self._enable_stereo(stereo_image) try: self._do_graphicsMode_Draw( for_mouseover_highlighting=True) finally: self._disable_stereo() except: print_compact_traceback( "exception in or around _do_graphicsMode_Draw() during GL_SELECT; " "ignored; restoring modelview matrix: ") glMatrixMode(GL_MODELVIEW) self._setup_modelview( ) ### REVIEW: correctness of this is unreviewed! # now it's important to continue, at least enough to restore other gl state self._frustum_planes_available = False # piotr 080331 # just to be safe and not use the frustum planes computed for # the pick matrix self.set_drawing_phase('?') self.current_glselect = False # REVIEW: On systems with no stencil buffer, I think we'd also need # to draw selobj here in highlighted form (in case that form is # bigger than when it's not highlighted), or (easier & faster) # just always pretend it passes the hit test and add it to # glselect_dict -- and, make sure to give it "first dibs" for being # the next selobj. I'll implement some of this now (untested when # no stencil buffer) but not yet all. [bruce 050612] selobj = self.selobj if selobj is not None: self.glselect_dict[id(selobj)] = selobj # (review: is the following note correct?) # note: unneeded, if the func that looks at this dict always # tries selobj first (except for a kluge near # "if self.glselect_dict", commented on below) glFlush() hit_records = list(glRenderMode(GL_RENDER)) if debugPicking: print "DLs %d hits" % len(hit_records) for (near, far, names) in hit_records: # see example code, renderpass.py ## print "hit record: near, far, names:", near, far, names # e.g. hit record: near, far, names: 1439181696 1453030144 (1638426L,) # which proves that near/far are too far apart to give actual depth, # in spite of the 1- or 3-pixel drawing window (presumably they're vertices # taken from unclipped primitives, not clipped ones). del near, far if 1: # partial workaround for bug 1527. This can be removed once that bug (in drawer.py) # is properly fixed. This exists in two places -- GLPane.py and modes.py. [bruce 060217] if names and names[-1] == 0: print "%d(g) partial workaround for bug 1527: removing 0 from end of namestack:" % env.redraw_counter, names names = names[:-1] if names: # For now, we only use the last element of names, # though (as of long before 080917) it is often longer: # - some code pushes the same name twice (directly and # via ColorSorter) (see 060725 debug print below); # - chunks push a name even when they draw atoms/bonds # which push their own names (see 080411 comment below). # # Someday: if we ever support "name/subname paths" we'll # probably let first name interpret the remaining ones. # In fact, if nodes change projection or viewport for # their kids, and/or share their kids, they'd need to # push their own names on the stack, so we'd know how # to redraw the kids, or which ones are meant when they # are shared. if debug_flags.atom_debug and len( names) > 1: # bruce 060725 if len(names) == 2 and names[0] == names[1]: if not env.seen_before( "dual-names bug" ): # this happens for Atoms (colorsorter bug??) print "debug (once-per-session message): why are some glnames duplicated on the namestack?", names else: # Note: as of sometime before 080411, this became common -- # I guess that chunks (which recently acquired glselect names) # are pushing their names even while drawing their atoms and bonds. # I am not sure if this can cause any problems -- almost surely not # directly, but maybe the nestedness of highlighted appearances could # violate some assumptions made by the highlight code... anyway, # to reduce verbosity I need to not print this when the deeper name # is that of a chunk, and there are exactly two names. [bruce 080411] if len(names) == 2 and \ isinstance( self.object_for_glselect_name(names[0]), self.assy.Chunk ): if not env.seen_before( "nested names for Chunk"): print "debug (once-per-session message): nested glnames for a Chunk: ", names else: print "debug fyi: len(names) == %d (names = %r)" % ( len(names), names) obj = self.object_for_glselect_name( names[-1]) #k should always return an obj if obj is None: print "bug: object_for_glselect_name returns None for name %r at end of namestack %r" % ( names[-1], names) else: self.glselect_dict[id(obj)] = obj # note: outside of this method, one of these will be # chosen to be saved as self.selobj and rerendered # in "highlighted" form ##if 0: ## # this debug print was useful for debugging bug 2945, ## # and when it happens it's usually a bug, ## # but not always: ## # - it's predicted to happen for ChunkDrawer._renderOverlayText ## # - and whenever we're using a whole-chunk display style ## # so we can't leave it in permanently. [bruce 081211] ## if isinstance( obj, self.assy.Chunk ): ## print "\n*** namestack topped with a chunk:", obj pass continue # next hit_record #e maybe we should now sort glselect_dict by "hit priority" # (for depth-tiebreaking), or at least put selobj first. # (or this could be done lower down, where it's used.) # [I think we do this now...] return # from do_glselect_if_wanted
def do_check_GL_support(force_enable): props = {} try: #log redirection: def redirect_log(logger_name): logger = logging.getLogger(logger_name) assert logger is not None logger.saved_handlers = logger.handlers logger.saved_propagate = logger.propagate logger.handlers = [CaptureHandler()] logger.propagate = 0 return logger fhlogger = redirect_log('OpenGL.formathandler') elogger = redirect_log('OpenGL.extensions') alogger = redirect_log('OpenGL.acceleratesupport') arlogger = redirect_log('OpenGL.arrays') clogger = redirect_log('OpenGL.converters') import OpenGL props["pyopengl"] = OpenGL.__version__ from OpenGL.GL import GL_VERSION, GL_EXTENSIONS from OpenGL.GL import glGetString, glGetInteger, glGetIntegerv gl_version_str = glGetString(GL_VERSION) if gl_version_str is None: gl_check_error("OpenGL version is missing - cannot continue") return {} gl_major = int(gl_version_str[0]) gl_minor = int(gl_version_str[2]) props["opengl"] = gl_major, gl_minor MIN_VERSION = (1,1) if (gl_major, gl_minor) < MIN_VERSION: gl_check_error("OpenGL output requires version %s or greater, not %s.%s" % (".".join([str(x) for x in MIN_VERSION]), gl_major, gl_minor)) else: log("found valid OpenGL version: %s.%s", gl_major, gl_minor) from OpenGL import version as OpenGL_version pyopengl_version = OpenGL_version.__version__ try: import OpenGL_accelerate accel_version = OpenGL_accelerate.__version__ props["accelerate"] = accel_version log("OpenGL_accelerate version %s", accel_version) except: log("OpenGL_accelerate not found") OpenGL_accelerate = None accel_version = None if accel_version is not None and pyopengl_version!=accel_version: global _version_warning_shown if not _version_warning_shown: log.warn("Warning: version mismatch between PyOpenGL and PyOpenGL-accelerate") log.warn(" this may cause crashes") _version_warning_shown = True vsplit = pyopengl_version.split('.') #we now require PyOpenGL 3.1 or later if vsplit[:2]<['3','1'] and not force_enable: gl_check_error("PyOpenGL version 3.1 or later is required (found version %s)" % pyopengl_version) return {} props["zerocopy"] = bool(OpenGL_accelerate) and is_pyopengl_memoryview_safe(pyopengl_version, accel_version) try: extensions = glGetString(GL_EXTENSIONS).decode().split(" ") except: log("error querying extensions", exc_info=True) extensions = [] gl_check_error("OpenGL could not find the list of GL extensions - does the graphics driver support OpenGL?") log("OpenGL extensions found: %s", ", ".join(extensions)) props["extensions"] = extensions from OpenGL.arrays.arraydatatype import ArrayDatatype try: log("found the following array handlers: %s", set(ArrayDatatype.getRegistry().values())) except: pass from OpenGL.GL import GL_RENDERER, GL_VENDOR, GL_SHADING_LANGUAGE_VERSION def fixstring(v): try: return str(v).strip() except: return str(v) for d,s,fatal in (("vendor", GL_VENDOR, True), ("renderer", GL_RENDERER, True), ("shading language version", GL_SHADING_LANGUAGE_VERSION, False)): try: v = glGetString(s) v = fixstring(v.decode()) log("%s: %s", d, v) except: if fatal: gl_check_error("OpenGL property '%s' is missing" % d) else: log("OpenGL property '%s' is missing", d) v = "" props[d] = v vendor = props["vendor"] version_req = VERSION_REQ.get(vendor) if version_req: req_maj, req_min = version_req if gl_major<req_maj or (gl_major==req_maj and gl_minor<req_min): if force_enable: log.warn("Warning: '%s' OpenGL driver requires version %i.%i", vendor, req_maj, req_min) log.warn(" version %i.%i was found", gl_major, gl_minor) else: gl_check_error("OpenGL version %i.%i is too old, %i.%i is required for %s" % (gl_major, gl_minor, req_maj, req_min, vendor)) from OpenGL.GLU import gluGetString, GLU_VERSION, GLU_EXTENSIONS for d,s in {"GLU version": GLU_VERSION, "GLU extensions":GLU_EXTENSIONS}.items(): v = gluGetString(s) v = v.decode() log("%s: %s", d, v) props[d] = v blacklisted = None whitelisted = None greylisted = None for k,vlist in BLACKLIST.items(): v = props.get(k) if v in vlist: log("%s '%s' found in blacklist: %s", k, v, vlist) blacklisted = k, v for k,vlist in GREYLIST.items(): v = props.get(k) if v in vlist: log("%s '%s' found in greylist: %s", k, v, vlist) greylisted = k, v for k,vlist in WHITELIST.items(): v = props.get(k) if v in vlist: log("%s '%s' found in whitelist: %s", k, v, vlist) whitelisted = k, v if blacklisted: if whitelisted: log.info("%s '%s' enabled (found in both blacklist and whitelist)", *whitelisted) elif force_enable: log.warn("Warning: %s '%s' is blacklisted!", *blacklisted) else: gl_check_error("%s '%s' is blacklisted!" % (blacklisted)) safe = bool(whitelisted) or not (bool(greylisted) or bool(blacklisted)) if safe and sys.version_info[0]>2: log.warn("Warning: OpenGL python3 support is not enabled by default") safe = False props["safe"] = safe #check for specific functions we need: from OpenGL.GL import glActiveTexture, glTexSubImage2D, glTexCoord2i, \ glViewport, glMatrixMode, glLoadIdentity, glOrtho, \ glEnableClientState, glGenTextures, glDisable, \ glBindTexture, glPixelStorei, glEnable, glBegin, glFlush, \ glTexParameteri, glTexEnvi, glHint, glBlendFunc, glLineStipple, \ glTexImage2D, \ glMultiTexCoord2i, \ glVertex2i, glEnd check_functions(glActiveTexture, glTexSubImage2D, glTexCoord2i, \ glViewport, glMatrixMode, glLoadIdentity, glOrtho, \ glEnableClientState, glGenTextures, glDisable, \ glBindTexture, glPixelStorei, glEnable, glBegin, glFlush, \ glTexParameteri, glTexEnvi, glHint, glBlendFunc, glLineStipple, \ glTexImage2D, \ glMultiTexCoord2i, \ glVertex2i, glEnd) glEnablei = None try: from OpenGL.GL import glEnablei except: pass if not bool(glEnablei): log.warn("OpenGL glEnablei is not available, disabling transparency") global GL_ALPHA_SUPPORTED GL_ALPHA_SUPPORTED = False props["transparency"] = GL_ALPHA_SUPPORTED #check for framebuffer functions we need: from OpenGL.GL.ARB.framebuffer_object import GL_FRAMEBUFFER, \ GL_COLOR_ATTACHMENT0, glGenFramebuffers, glBindFramebuffer, glFramebufferTexture2D check_functions(GL_FRAMEBUFFER, \ GL_COLOR_ATTACHMENT0, glGenFramebuffers, glBindFramebuffer, glFramebufferTexture2D) for ext in required_extensions: if ext not in extensions: gl_check_error("OpenGL driver lacks support for extension: %s" % ext) else: log("Extension %s is present", ext) #this allows us to do CSC via OpenGL: #see http://www.opengl.org/registry/specs/ARB/fragment_program.txt from OpenGL.GL.ARB.fragment_program import glInitFragmentProgramARB if not glInitFragmentProgramARB(): gl_check_error("OpenGL output requires glInitFragmentProgramARB") else: log("glInitFragmentProgramARB works") from OpenGL.GL.ARB.texture_rectangle import glInitTextureRectangleARB if not glInitTextureRectangleARB(): gl_check_error("OpenGL output requires glInitTextureRectangleARB") else: log("glInitTextureRectangleARB works") from OpenGL.GL.ARB.vertex_program import glGenProgramsARB, glDeleteProgramsARB, \ glBindProgramARB, glProgramStringARB check_functions(glGenProgramsARB, glDeleteProgramsARB, glBindProgramARB, glProgramStringARB) try: from OpenGL.GL import GL_MAX_TEXTURE_SIZE texture_size = glGetInteger(GL_MAX_TEXTURE_SIZE) #this one may be missing? rect_texture_size = texture_size try: from OpenGL.GL import GL_MAX_RECTANGLE_TEXTURE_SIZE rect_texture_size = glGetInteger(GL_MAX_RECTANGLE_TEXTURE_SIZE) except ImportError as e: log("OpenGL: %s", e) log("using GL_MAX_TEXTURE_SIZE=%s as default", texture_size) except Exception as e: emsg = str(e) if hasattr(e, "description"): emsg = e.description gl_check_error("unable to query max texture size: %s" % emsg) return props log("Texture size GL_MAX_RECTANGLE_TEXTURE_SIZE=%s, GL_MAX_TEXTURE_SIZE=%s", rect_texture_size, texture_size) texture_size_limit = min(rect_texture_size, texture_size) props["texture-size-limit"] = texture_size_limit try: from OpenGL.GL import GL_MAX_VIEWPORT_DIMS v = glGetIntegerv(GL_MAX_VIEWPORT_DIMS) max_viewport_dims = v[0], v[1] assert max_viewport_dims[0]>=texture_size_limit and max_viewport_dims[1]>=texture_size_limit log("GL_MAX_VIEWPORT_DIMS=%s", max_viewport_dims) except ImportError as e: log.error("Error querying max viewport dims: %s", e) max_viewport_dims = texture_size_limit, texture_size_limit props["max-viewport-dims"] = max_viewport_dims return props finally: for x in alogger.handlers[0].records: #strip default message prefix: msg = x.getMessage().replace("No OpenGL_accelerate module loaded: ", "") if msg=="No module named OpenGL_accelerate": msg = "missing accelerate module" if msg!="OpenGL_accelerate module loaded": msg = "PyOpenGL warning: %s" % msg log.info(msg) #format handler messages: STRIP_LOG_MESSAGE = "Unable to load registered array format handler " missing_handlers = [] for x in fhlogger.handlers[0].records: msg = x.getMessage() p = msg.find(STRIP_LOG_MESSAGE) if p<0: #unknown message, log it: log.info(msg) continue format_handler = msg[p+len(STRIP_LOG_MESSAGE):] p = format_handler.find(":") if p>0: format_handler = format_handler[:p] missing_handlers.append(format_handler) if len(missing_handlers)>0: log.warn("PyOpenGL warning: missing array format handlers: %s", ", ".join(missing_handlers)) for x in elogger.handlers[0].records: msg = x.getMessage() #ignore extension messages: p = msg.startswith("GL Extension ") and msg.endswith("available") if not p: log.info(msg) missing_accelerators = [] STRIP_AR_HEAD = "Unable to load" STRIP_AR_TAIL = "from OpenGL_accelerate" for x in arlogger.handlers[0].records+clogger.handlers[0].records: msg = x.getMessage() if msg.startswith(STRIP_AR_HEAD) and msg.endswith(STRIP_AR_TAIL): m = msg[len(STRIP_AR_HEAD):-len(STRIP_AR_TAIL)].strip() m = m.replace("accelerators", "").replace("accelerator", "").strip() missing_accelerators.append(m) continue elif msg.startswith("Using accelerated"): log(msg) else: log.info(msg) if missing_accelerators: log.info("OpenGL accelerate missing: %s", ", ".join(missing_accelerators)) def restore_logger(logger): logger.handlers = logger.saved_handlers logger.propagate = logger.saved_propagate restore_logger(fhlogger) restore_logger(elogger) restore_logger(alogger) restore_logger(arlogger) restore_logger(clogger)
def check_PyOpenGL_support(force_enable): props = {} try: if CRASH: import ctypes ctypes.string_at(0) raise Exception("should have crashed!") elif TIMEOUT > 0: import time time.sleep(TIMEOUT) #log redirection: def redirect_log(logger_name): logger = logging.getLogger(logger_name) assert logger is not None logger.saved_handlers = logger.handlers logger.saved_propagate = logger.propagate logger.handlers = [CaptureHandler()] logger.propagate = 0 return logger fhlogger = redirect_log('OpenGL.formathandler') elogger = redirect_log('OpenGL.extensions') alogger = redirect_log('OpenGL.acceleratesupport') arlogger = redirect_log('OpenGL.arrays') clogger = redirect_log('OpenGL.converters') import OpenGL props["pyopengl"] = OpenGL.__version__ from OpenGL.GL import GL_VERSION, GL_EXTENSIONS from OpenGL.GL import glGetString, glGetInteger, glGetIntegerv gl_version_str = glGetString(GL_VERSION) if gl_version_str is None: raise_fatal_error("OpenGL version is missing - cannot continue") return {} gl_major = int(bytestostr(gl_version_str)[0]) gl_minor = int(bytestostr(gl_version_str)[2]) props["opengl"] = gl_major, gl_minor MIN_VERSION = (1, 1) if (gl_major, gl_minor) < MIN_VERSION: raise_fatal_error( "OpenGL output requires version %s or greater, not %s.%s" % (".".join([str(x) for x in MIN_VERSION]), gl_major, gl_minor)) else: log("found valid OpenGL version: %s.%s", gl_major, gl_minor) from OpenGL import version as OpenGL_version pyopengl_version = OpenGL_version.__version__ try: import OpenGL_accelerate #@UnresolvedImport accel_version = OpenGL_accelerate.__version__ props["accelerate"] = accel_version log("OpenGL_accelerate version %s", accel_version) except: log("OpenGL_accelerate not found") OpenGL_accelerate = None accel_version = None if accel_version is not None and pyopengl_version != accel_version: global _version_warning_shown if not _version_warning_shown: log.warn( "Warning: version mismatch between PyOpenGL and PyOpenGL-accelerate" ) log.warn(" %s vs %s", pyopengl_version, accel_version) log.warn(" this may cause crashes") _version_warning_shown = True gl_check_error( "PyOpenGL vs accelerate version mismatch: %s vs %s" % (pyopengl_version, accel_version)) vsplit = pyopengl_version.split('.') #we now require PyOpenGL 3.1 or later if vsplit[:3] < ['3', '1'] and not force_enable: raise_fatal_error("PyOpenGL version %s is too old and buggy" % pyopengl_version) return {} props["zerocopy"] = bool( OpenGL_accelerate) and is_pyopengl_memoryview_safe( pyopengl_version, accel_version) try: extensions = glGetString(GL_EXTENSIONS).decode().split(" ") except: log("error querying extensions", exc_info=True) extensions = [] raise_fatal_error( "OpenGL could not find the list of GL extensions - does the graphics driver support OpenGL?" ) log("OpenGL extensions found: %s", csv(extensions)) props["extensions"] = extensions from OpenGL.arrays.arraydatatype import ArrayDatatype try: log("found the following array handlers: %s", set(ArrayDatatype.getRegistry().values())) except: pass from OpenGL.GL import GL_RENDERER, GL_VENDOR, GL_SHADING_LANGUAGE_VERSION def fixstring(v): try: return str(v).strip() except: return str(v) for d, s, fatal in (("vendor", GL_VENDOR, True), ("renderer", GL_RENDERER, True), ("shading-language-version", GL_SHADING_LANGUAGE_VERSION, False)): try: v = glGetString(s) v = fixstring(v.decode()) log("%s: %s", d, v) except: if fatal: gl_check_error("OpenGL property '%s' is missing" % d) else: log("OpenGL property '%s' is missing", d) v = "" props[d] = v vendor = props["vendor"] version_req = VERSION_REQ.get(vendor) if version_req: req_maj, req_min = version_req if gl_major < req_maj or (gl_major == req_maj and gl_minor < req_min): if force_enable: log.warn( "Warning: '%s' OpenGL driver requires version %i.%i", vendor, req_maj, req_min) log.warn(" version %i.%i was found", gl_major, gl_minor) else: gl_check_error( "OpenGL version %i.%i is too old, %i.%i is required for %s" % (gl_major, gl_minor, req_maj, req_min, vendor)) from OpenGL.GLU import gluGetString, GLU_VERSION, GLU_EXTENSIONS for d, s in { "GLU.version": GLU_VERSION, "GLU.extensions": GLU_EXTENSIONS }.items(): v = gluGetString(s) v = v.decode() log("%s: %s", d, v) props[d] = v def match_list(thelist, listname): for k, vlist in thelist.items(): v = props.get(k) matches = [x for x in vlist if v.find(x) >= 0] if matches: log("%s '%s' found in %s: %s", k, v, listname, vlist) return (k, v) log("%s '%s' not found in %s: %s", k, v, listname, vlist) return None blacklisted = match_list(BLACKLIST, "blacklist") greylisted = match_list(GREYLIST, "greylist") whitelisted = match_list(WHITELIST, "whitelist") if blacklisted: if whitelisted: log.info( "%s '%s' enabled (found in both blacklist and whitelist)", *whitelisted) elif force_enable: log.warn("Warning: %s '%s' is blacklisted!", *blacklisted) log.warn(" force enabled by option") else: raise_fatal_error("%s '%s' is blacklisted!" % (blacklisted)) safe = bool(whitelisted) or not bool(blacklisted) if greylisted and not whitelisted: log.warn("Warning: %s '%s' is greylisted,", *greylisted) log.warn(" you may want to turn off OpenGL if you encounter bugs") props["safe"] = safe #check for specific functions we need: from OpenGL.GL import glActiveTexture, glTexSubImage2D, glTexCoord2i, \ glViewport, glMatrixMode, glLoadIdentity, glOrtho, \ glEnableClientState, glGenTextures, glDisable, \ glBindTexture, glPixelStorei, glEnable, glBegin, glFlush, \ glTexParameteri, glTexEnvi, glHint, glBlendFunc, glLineStipple, \ glTexImage2D, \ glMultiTexCoord2i, \ glVertex2i, glEnd check_functions(glActiveTexture, glTexSubImage2D, glTexCoord2i, \ glViewport, glMatrixMode, glLoadIdentity, glOrtho, \ glEnableClientState, glGenTextures, glDisable, \ glBindTexture, glPixelStorei, glEnable, glBegin, glFlush, \ glTexParameteri, glTexEnvi, glHint, glBlendFunc, glLineStipple, \ glTexImage2D, \ glMultiTexCoord2i, \ glVertex2i, glEnd) #check for framebuffer functions we need: from OpenGL.GL.ARB.framebuffer_object import GL_FRAMEBUFFER, \ GL_COLOR_ATTACHMENT0, glGenFramebuffers, glBindFramebuffer, glFramebufferTexture2D check_functions(GL_FRAMEBUFFER, \ GL_COLOR_ATTACHMENT0, glGenFramebuffers, glBindFramebuffer, glFramebufferTexture2D) glEnablei = None try: from OpenGL.GL import glEnablei except: pass if not bool(glEnablei): log.warn( "OpenGL glEnablei is not available, disabling transparency") global GL_ALPHA_SUPPORTED GL_ALPHA_SUPPORTED = False props["transparency"] = GL_ALPHA_SUPPORTED for ext in required_extensions: if ext not in extensions: raise_fatal_error( "OpenGL driver lacks support for extension: %s" % ext) else: log("Extension %s is present", ext) #this allows us to do CSC via OpenGL: #see http://www.opengl.org/registry/specs/ARB/fragment_program.txt from OpenGL.GL.ARB.fragment_program import glInitFragmentProgramARB if not glInitFragmentProgramARB(): raise_fatal_error( "OpenGL output requires glInitFragmentProgramARB") else: log("glInitFragmentProgramARB works") from OpenGL.GL.ARB.texture_rectangle import glInitTextureRectangleARB if not glInitTextureRectangleARB(): raise_fatal_error( "OpenGL output requires glInitTextureRectangleARB") else: log("glInitTextureRectangleARB works") from OpenGL.GL.ARB.vertex_program import glGenProgramsARB, glDeleteProgramsARB, \ glBindProgramARB, glProgramStringARB check_functions(glGenProgramsARB, glDeleteProgramsARB, glBindProgramARB, glProgramStringARB) try: from OpenGL.GL import GL_MAX_TEXTURE_SIZE texture_size = glGetInteger(GL_MAX_TEXTURE_SIZE) #this one may be missing? rect_texture_size = texture_size try: from OpenGL.GL import GL_MAX_RECTANGLE_TEXTURE_SIZE rect_texture_size = glGetInteger(GL_MAX_RECTANGLE_TEXTURE_SIZE) except ImportError as e: log("OpenGL: %s", e) log("using GL_MAX_TEXTURE_SIZE=%s as default", texture_size) except Exception as e: emsg = str(e) if hasattr(e, "description"): emsg = e.description gl_check_error("unable to query max texture size: %s" % emsg) return props log( "Texture size GL_MAX_RECTANGLE_TEXTURE_SIZE=%s, GL_MAX_TEXTURE_SIZE=%s", rect_texture_size, texture_size) texture_size_limit = min(rect_texture_size, texture_size) props["texture-size-limit"] = int(texture_size_limit) try: from OpenGL.GL import GL_MAX_VIEWPORT_DIMS v = glGetIntegerv(GL_MAX_VIEWPORT_DIMS) max_viewport_dims = int(v[0]), int(v[1]) assert max_viewport_dims[ 0] >= texture_size_limit and max_viewport_dims[ 1] >= texture_size_limit log("GL_MAX_VIEWPORT_DIMS=%s", max_viewport_dims) except ImportError as e: log.error("Error querying max viewport dims: %s", e) max_viewport_dims = texture_size_limit, texture_size_limit props["max-viewport-dims"] = max_viewport_dims return props finally: for x in alogger.handlers[0].records: #strip default message prefix: msg = x.getMessage().replace( "No OpenGL_accelerate module loaded: ", "") if msg == "No module named OpenGL_accelerate": msg = "missing accelerate module" if msg == "OpenGL_accelerate module loaded": log.info(msg) else: log.warn("PyOpenGL warning: %s", msg) #format handler messages: STRIP_LOG_MESSAGE = "Unable to load registered array format handler " missing_handlers = [] for x in fhlogger.handlers[0].records: msg = x.getMessage() p = msg.find(STRIP_LOG_MESSAGE) if p < 0: #unknown message, log it: log.info(msg) continue format_handler = msg[p + len(STRIP_LOG_MESSAGE):] p = format_handler.find(":") if p > 0: format_handler = format_handler[:p] missing_handlers.append(format_handler) if len(missing_handlers) > 0: log.warn("PyOpenGL warning: missing array format handlers: %s", csv(missing_handlers)) for x in elogger.handlers[0].records: msg = x.getMessage() #ignore extension messages: p = msg.startswith("GL Extension ") and msg.endswith("available") if not p: log.info(msg) missing_accelerators = [] STRIP_AR_HEAD = "Unable to load" STRIP_AR_TAIL = "from OpenGL_accelerate" for x in arlogger.handlers[0].records + clogger.handlers[0].records: msg = x.getMessage() if msg.startswith(STRIP_AR_HEAD) and msg.endswith(STRIP_AR_TAIL): m = msg[len(STRIP_AR_HEAD):-len(STRIP_AR_TAIL)].strip() m = m.replace("accelerators", "").replace("accelerator", "").strip() missing_accelerators.append(m) continue elif msg.startswith("Using accelerated"): log(msg) else: log.info(msg) if missing_accelerators: log.info("OpenGL accelerate missing: %s", csv(missing_accelerators)) def restore_logger(logger): logger.handlers = logger.saved_handlers logger.propagate = logger.saved_propagate restore_logger(fhlogger) restore_logger(elogger) restore_logger(alogger) restore_logger(arlogger) restore_logger(clogger)
def _setup_projection(self, glselect=False): ### WARNING: This is not actually private! TODO: rename it. """ Set up standard projection matrix contents using various attributes of self (aspect, vdist, scale, zoomFactor). Also reads the current OpenGL viewport bounds in window coordinates. (Warning: leaves matrixmode as GL_PROJECTION.) @param glselect: False (default) normally, or a 4-tuple (format not documented here) to prepare for GL_SELECT picking by calling gluPickMatrix(). If you are really going to draw in the pick window (for GL_RENDER and glReadPixels picking, rather than GL_SELECT picking), don't forget to set the glViewport *after* calling _setup_projection. Here's why: gluPickMatrix needs to know the current *whole-window* viewport, in order to set up a projection matrix to map a small portion of it to the clipping boundaries for GL_SELECT. From the gluPickMatrix doc page: viewport: Specifies the current viewport (as from a glGetIntegerv call). Description: gluPickMatrix creates a projection matrix that can be used to restrict drawing to a small region of the viewport. In the graphics pipeline, the clipper actually works in homogeneous coordinates, clipping polygons to the {X,Y}==+-W boundaries. This saves the work of doing the homogeneous division: {X,Y}/W==+-1.0, (and avoiding problems when W is zero for points on the eye plane in Z,) but it comes down to the same thing as clipping to X,Y==+-1 in orthographic. So the projection matrix decides what 3D model-space planes map to +-1 in X,Y. (I think it maps [near,far] to [0,1] in Z, because they're not clipped symmetrically.) Then glViewport sets the hardware transform that determines where the +-1 square of clipped output goes in screen pixels within the window. Normally you don't actually draw pixels while picking in GL_SELECT mode because the pipeline outputs hits after the clipping stage, so gluPickMatrix only reads the viewport and sets the projection matrix. """ #bruce 080912 moved this from GLPane into GLPane_minimal glMatrixMode(GL_PROJECTION) glLoadIdentity() scale = self.scale * self.zoomFactor near, far = self.near, self.far aspect = self.aspect vdist = self.vdist if glselect: x, y, w, h = glselect gluPickMatrix( x, y, w, h, glGetIntegerv( GL_VIEWPORT ) #k is this arg needed? it might be the default... ) if self.ortho: glOrtho(-scale * aspect, scale * aspect, -scale, scale, vdist * near, vdist * far) else: glFrustum(-scale * near * aspect, scale * near * aspect, -scale * near, scale * near, vdist * near, vdist * far) return
def set_data(self, data, **kwargs): '''Associates N-D point data with the window. ARGUMENTS: data (numpy.ndarray): An RxCxB array of data points to display. KEYWORD ARGUMENTS: classes (numpy.ndarray): An RxC array of integer class labels (zeros means unassigned). features (list): Indices of feautures to display in the octant (see NDWindow.set_octant_display_features for description). ''' import OpenGL.GL as gl try: from OpenGL.GL import glGetIntegerv except: from OpenGL.GL.glget import glGetIntegerv classes = kwargs.get('classes', None) features = kwargs.get('features', list(range(6))) if self.data.shape[2] < 6: features = features[:3] self.quadrant_mode == 'single' # Scale the data set to span an octant data2d = np.array(data.reshape((-1, data.shape[-1]))) mins = np.min(data2d, axis=0) maxes = np.max(data2d, axis=0) denom = (maxes - mins).astype(float) denom = np.where(denom > 0, denom, 1.0) self.data = (data2d - mins) / denom self.data.shape = data.shape self.palette = spy_colors.astype(float) / 255. self.palette[0] = np.array([1.0, 1.0, 1.0]) self.colors = self.palette[self.classes.ravel()].reshape( self.data.shape[:2] + (3, )) self.colors = (self.colors * 255).astype('uint8') colors = np.ones((self.colors.shape[:-1]) + (4, ), 'uint8') colors[:, :, :-1] = self.colors self.colors = colors self._refresh_display_lists = True self.set_octant_display_features(features) # Determine the bit masks to use when using RGBA components for # identifying pixel IDs. components = [ gl.GL_RED_BITS, gl.GL_GREEN_BITS, gl.GL_GREEN_BITS, gl.GL_ALPHA_BITS ] self._rgba_bits = [min(8, glGetIntegerv(i)) for i in components] self._low_bits = [min(8, 8 - self._rgba_bits[i]) for i in range(4)] self._rgba_masks = \ [(2**self._rgba_bits[i] - 1) << (8 - self._rgba_bits[i]) for i in range(4)] # Determine how many times the scene will need to be rendered in the # background to extract the pixel's row/col index. N = self.data.shape[0] * self.data.shape[1] if N > 2**sum(self._rgba_bits): raise Exception( 'Insufficient color bits (%d) for N-D window display' % sum(self._rgba_bits)) self.reset_view_geometry()
desc = string.split(module_name, '.', 1)[1] f.write('<table border="1" cellspacing="0" cellpadding="2"><thead><tr><h2>%s</h2></tr></thead>' % desc) for i in info[module_name]: try: if len(i) == 2: key, value = i else: key, id, t = i if t[0] == 'b': value = glGetBooleanv(id) if operator.isSequence(value): value = map(_boolean, value) else: value = _boolean(value) elif t[0] == 'i': value = glGetIntegerv(id) elif t[0] == 'd': value = glGetDoublev(id) elif t[0] == 'e': if len(t) > 1: if t[1] == 'u': x = gluGetString(id) else: x = glGetString(id) else: x = id x = string.split(x) x.sort() y = [] for ext in x: try:
def set_data(self, data, **kwargs): '''Associates N-D point data with the window. ARGUMENTS: data (numpy.ndarray): An RxCxB array of data points to display. KEYWORD ARGUMENTS: classes (numpy.ndarray): An RxC array of integer class labels (zeros means unassigned). features (list): Indices of feautures to display in the octant (see NDWindow.set_octant_display_features for description). ''' import OpenGL.GL as gl try: from OpenGL.GL import glGetIntegerv except: from OpenGL.GL.glget import glGetIntegerv classes = kwargs.get('classes', None) features = kwargs.get('features', list(range(6))) if self.data.shape[2] < 6: features = features[:3] self.quadrant_mode == 'single' # Scale the data set to span an octant data2d = np.array(data.reshape((-1, data.shape[-1]))) mins = np.min(data2d, axis=0) maxes = np.max(data2d, axis=0) denom = (maxes - mins).astype(float) denom = np.where(denom > 0, denom, 1.0) self.data = (data2d - mins) / denom self.data.shape = data.shape self.palette = spy_colors.astype(float) / 255. self.palette[0] = np.array([1.0, 1.0, 1.0]) self.colors = self.palette[self.classes.ravel()].reshape( self.data.shape[:2] + (3,)) self.colors = (self.colors * 255).astype('uint8') colors = np.ones((self.colors.shape[:-1]) + (4,), 'uint8') colors[:, :, :-1] = self.colors self.colors = colors self._refresh_display_lists = True self.set_octant_display_features(features) # Determine the bit masks to use when using RGBA components for # identifying pixel IDs. components = [gl.GL_RED_BITS, gl.GL_GREEN_BITS, gl.GL_GREEN_BITS, gl.GL_ALPHA_BITS] self._rgba_bits = [min(8, glGetIntegerv(i)) for i in components] self._low_bits = [min(8, 8 - self._rgba_bits[i]) for i in range(4)] self._rgba_masks = \ [(2**self._rgba_bits[i] - 1) << (8 - self._rgba_bits[i]) for i in range(4)] # Determine how many times the scene will need to be rendered in the # background to extract the pixel's row/col index. N = self.data.shape[0] * self.data.shape[1] if N > 2**sum(self._rgba_bits): raise Exception('Insufficient color bits (%d) for N-D window display' % sum(self._rgba_bits)) self.reset_view_geometry()
def draw_primitives(self, scalefactor=1.0, center=[0.0, 0.0, 0.0], recenter=False, want_camera=False): # measure the bounding box of all our primitives, so that we can # recenter them in our field of view if recenter: all_meshes = self.static_meshes + self.dynamic_meshes all_lines = self.static_lines + self.dynamic_lines if (len(all_meshes) + len(all_lines)) == 0: if want_camera: return { 'modelview_matrix': glGetDoublev(GL_MODELVIEW_MATRIX), 'projection_matrix': glGetDoublev(GL_PROJECTION_MATRIX), 'viewport': glGetIntegerv(GL_VIEWPORT) } else: return None for m in all_meshes: m.v = m.v.reshape((-1, 3)) all_verts = np.concatenate([ m.v[m.f.flatten() if len(m.f) > 0 else np.arange(len(m.v))] for m in all_meshes ] + [l.v[l.e.flatten()] for l in all_lines], axis=0) maximum = np.max(all_verts, axis=0) minimum = np.min(all_verts, axis=0) center = (maximum + minimum) / 2. scalefactor = (maximum - minimum) / 4. scalefactor = np.max(scalefactor) else: center = np.array(center) # for mesh in self.dynamic_meshes : # if mesh.f : mesh.reset_normals() all_meshes = self.static_meshes + self.dynamic_meshes all_lines = self.static_lines + self.dynamic_lines self.current_center = center self.current_scalefactor = scalefactor glMatrixMode(GL_MODELVIEW) glPushMatrix() # uncomment to add a default rotation (useful when automatically snapshoting kinect data # glRotate(220, 0.0, 1.0, 0.0) tf = np.identity(4, 'f') / scalefactor tf[:3, 3] = -center / scalefactor tf[3, 3] = 1 cur_mtx = glGetFloatv(GL_MODELVIEW_MATRIX).T glLoadMatrixf(cur_mtx.dot(tf).T) if want_camera: result = { 'modelview_matrix': glGetDoublev(GL_MODELVIEW_MATRIX), 'projection_matrix': glGetDoublev(GL_PROJECTION_MATRIX), 'viewport': glGetIntegerv(GL_VIEWPORT) } else: result = None for m in all_meshes: if not hasattr(m, 'vbo'): # Precompute vertex vbo fidxs = m.f.flatten() if len(m.f) > 0 else np.arange(len(m.v)) allpts = m.v[fidxs].astype(np.float32).flatten() vbo = OpenGL.arrays.vbo.VBO(allpts) m.vbo = {'v': vbo} # Precompute normals vbo if hasattr(m, 'vn'): ns = m.vn.astype(np.float32) ns = ns[m.f.flatten(), :] m.vbo['vn'] = OpenGL.arrays.vbo.VBO(ns.flatten()) elif hasattr(m, 'f') and m.f.size > 0: ns = TriNormals(m.v, m.f).reshape(-1, 3) ns = np.tile(ns, (1, 3)).reshape(-1, 3).astype(np.float32) m.vbo['vn'] = OpenGL.arrays.vbo.VBO(ns.flatten()) # Precompute texture vbo if hasattr(m, 'ft') and (m.ft.size > 0): ftidxs = m.ft.flatten() data = m.vt[ftidxs].astype(np.float32)[:, 0:2] data[:, 1] = 1.0 - 1.0 * data[:, 1] m.vbo['vt'] = OpenGL.arrays.vbo.VBO(data) # Precompute color vbo if hasattr(m, 'vc'): data = m.vc[fidxs].astype(np.float32) m.vbo['vc'] = OpenGL.arrays.vbo.VBO(data) elif hasattr(m, 'fc'): data = np.tile(m.fc, (1, 3)).reshape(-1, 3).astype(np.float32) m.vbo['vc'] = OpenGL.arrays.vbo.VBO(data) for e in all_lines: self.draw_lines(e) for m in all_meshes: if hasattr(m, 'texture_image') and not hasattr(m, 'textureID'): self.set_texture(m) self.draw_mesh(m, self.lighting_on) glMatrixMode(GL_MODELVIEW) glPopMatrix() return result