Ejemplo n.º 1
0
def stencilPush():
    '''Create a new stack in stencil stack.
    All the next draw will be done in stencil buffer until
    stencilUse() will be called.'''
    global __stencil_stack
    glPushAttrib(GL_STENCIL_BUFFER_BIT | GL_STENCIL_TEST)

    # enable stencil test if not yet enabled
    if not glIsEnabled(GL_STENCIL_TEST):
        glClearStencil(0)
        glClear(GL_STENCIL_BUFFER_BIT)
        glEnable(GL_STENCIL_TEST)

    # increment the draw buffer
    glStencilFunc(GL_NEVER, 0x0, 0x0)
    glStencilOp(GL_INCR, GL_INCR, GL_INCR)
    glColorMask(0, 0, 0, 0)

    # save model view
    m = glGetFloatv(GL_MODELVIEW_MATRIX)
    __stencil_stack_view.append(m)

    # start recording GL operation
    dl = GlDisplayList()
    dl.start()
    __stencil_stack_dl.append(dl)

    __stencil_stack += 1
Ejemplo n.º 2
0
 def views(self, camera):
     # Left eye view red
     self.set_defaults(camera)
     camera.eye_pos = -1.0 # left eye
     glColorMask(False, True, False, True) # Red
     with camera:
         yield camera
     # Right eye view cyan
     camera.eye_pos = +1.0 # right eye
     glColorMask(True, False, True, True)
     with camera:
         yield camera
Ejemplo n.º 3
0
def stencilUse():
    '''Switch from stencil draw to color draw.
    Now, all drawing will be done on color buffer,
    using latest stencil stack.
    '''
    # stop recording gl operation
    __stencil_stack_dl[__stencil_stack-1].stop()
    __stencil_stack_dl[__stencil_stack-1].draw()

    # draw inner content only when stencil match the buffer
    glColorMask(1, 1, 1, 1)
    glStencilFunc(GL_EQUAL, __stencil_stack, __stencil_stack)
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
Ejemplo n.º 4
0
def stencilPop():
    '''Pop out the last stack from stencil stack'''
    global __stencil_stack
    glPopAttrib()
    __stencil_stack -= 1

    # remove current stencil stack
    __stencil_stack_dl.pop()
    __stencil_stack_view.pop()

    # replay stencil stack from the start
    # only if it's enabled
    if not glIsEnabled(GL_STENCIL_TEST):
        return

    # clear stencil
    glClearStencil(0)
    glClear(GL_STENCIL_BUFFER_BIT)

    # increment the draw buffer
    glStencilFunc(GL_NEVER, 0x0, 0x0)
    glStencilOp(GL_INCR, GL_INCR, GL_INCR)
    glColorMask(0, 0, 0, 0)

    # replay all gl operation
    for idx in xrange(__stencil_stack):
        dl = __stencil_stack_dl[idx]
        view = __stencil_stack_view[idx]
        with gx_matrix_identity:
            glMultMatrixf(view)
            dl.draw()

    # draw inner content only when stencil match the buffer
    glColorMask(1, 1, 1, 1)
    glStencilFunc(GL_EQUAL, __stencil_stack, __stencil_stack)
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
    def _disable_stereo(self):
        """
        Disables stereo rendering.
        This method pops a modelview matrix from the matrix stack.
        """
        if not self.stereo_enabled:
            return

        if self.stereo_mode <= 2:
            # side-by-side stereo mode
            # make sure that the clipping plane is disabled
            glDisable(GL_CLIP_PLANE5)
        else:
            # anaglyphs stereo mode
            # enable all colors
            glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)

        # restore the matrix
        glMatrixMode(GL_MODELVIEW)
        glPopMatrix()

        glPopAttrib()

        return
Ejemplo n.º 6
0
 def disable_color(self):  ### really should be a glpane method
     "don't draw color pixels (but keep drawing depth pixels, if you were)"
     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
     pass
Ejemplo n.º 7
0
    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
Ejemplo n.º 8
0
    def preDraw_glselect_dict(self):  #bruce 050609
        """
        Assume the depth buffer is clear.
        Draw each object in self.glselect_dict (in a certain order)
        (drawing them into depth buffer only) and measure its depth,
        until you find one near (undeep) enough (at the target pixel)
        to account for the target depth (self.targetdepth). Return
        that one, but first clear the depth buffer again.

        @note: some details are not documented here; see the code.
        """
        # We need to draw glselect_dict objects separately,
        # so their drawing code runs now rather than in the past
        # (when some display list was being compiled),
        # so they can notice they're in that dict.
        # We also draw them first, so that the nearest one
        # (or one of them, if there's a tie)
        # is sure to update the depth buffer.
        # (Then we clear the depth buffer, so that this drawing
        #  doesn't mess up later drawing at the same depth.)
        # (If some mode with special drawing code wants to join this system,
        #  it should be refactored to store special nodes in the model
        #  which can be drawn in the standard way.)
        glMatrixMode(GL_MODELVIEW)
        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
        # optimization -- don't draw color pixels (depth is all we need)
        newpicked = None  # in case of errors, and to record found object
        # here we should sort the objs to check the ones we most want first
        # (especially selobj)...
        #bruce 050702 try sorting this, see if it helps pick bonds rather than
        # invisible selatoms -- it seems to help.
        # This removes a bad side effect of today's earlier fix of bug 715-1.
        objects = self.glselect_dict.values()
        items = []  # (order, obj) pairs, for sorting objects
        for obj in objects:
            ### MAYBE TODO: add a special order for the object found previously
            # by the glname-to-color code for shader primitives.
            # But only do this after we're sure we draw all other objects
            # then too. And its order should be worse than selobj, I think.
            # [bruce 090105 comment]
            if obj is self.selobj:
                order = 0
            elif isinstance(obj, Bond):
                #bruce 080402 precaution: don't say obj.__class__ is Bond,
                # in case obj has no __class__
                order = 1
            else:
                order = 2
            order = (order, id(obj))
            #bruce 080402 work around bug in Bond.__eq__ for bonds not on
            # the same atom; later on 080402 I fixed that bug, but I'll
            # leave this for safety in case of __eq__ bugs on other kinds
            # of selobjs (e.g. dependence on other.__class__)
            items.append((order, obj))
        items.sort()
        report_failures = debug_pref(
            "GLPane: preDraw_glselect_dict: report failures?",
            Choice_boolean_False,
            prefs_key=True)
        if debug_pref("GLPane: check_target_depth debug prints?",
                      Choice_boolean_False,
                      prefs_key=True):
            debug_prefix = "check_target_depth"
        else:
            debug_prefix = None
        fudge = self.graphicsMode.check_target_depth_fudge_factor  #bruce 070115 kluge for testmode
        ### REVIEW: should this be an attribute of each object which can be drawn as selobj, instead?
        # The reasons it's needed are the same ones that require a nonzero DEPTH_TWEAK in GLPane_minimal.
        # See also the comment about it inside check_target_depth. [bruce 070921 comment]
        for orderjunk, obj in items:  # iterate over candidates
            try:
                method = obj.draw_in_abs_coords
            except AttributeError:
                print "bug? ignored: %r has no draw_in_abs_coords method" % (
                    obj, )
                print "   items are:", items
            else:
                try:
                    colorsorter_safe = getattr(obj, '_selobj_colorsorter_safe',
                                               False)
                    # colorsorter_safe being False (in two places in this file)
                    # is a bugfix for the breaking of expr handle highlighting
                    # by the debug_pref('GLPane: highlight atoms in CSDLs?')
                    # (but bug's cause is not understood) [bruce 090311]
                    for stereo_image in self.stereo_images_to_draw:
                        # REVIEW: would it be more efficient, and correct,
                        # to iterate over stereo images outside, and candidates
                        # inside (i.e. turn this pair of loops inside out)?
                        # I guess that would require knowing which stereo_image
                        # we're sampling in... and in that case we'd want to use
                        # only one of them anyway to do the testing
                        # (probably even if they overlap, just pick one and
                        # use that one -- see related comments in _enable_stereo).
                        # [bruce 080911 comment]
                        self._enable_stereo(stereo_image)

                        self.set_drawing_phase(
                            'selobj/preDraw_glselect_dict')  # bruce 070124

                        def func():
                            method(self, white)
                            # draw depth info
                            # (color doesn't matter since we're not drawing pixels)
                            return

                        self._call_func_that_draws_objects(
                            func, self.part, bare_primitives=colorsorter_safe)

                        self._disable_stereo()

                    self.set_drawing_phase('?')
                    #bruce 050822 changed black to white in case some draw methods have boolean-test bugs for black (unlikely)
                    ###@@@ in principle, this needs bugfixes; in practice the bugs are tolerable in the short term
                    # (see longer discussion in other comments):
                    # - if no one reaches target depth, or more than one does, be smarter about what to do?
                    # - try current selobj first [this is done, as of 050702],
                    #   or give it priority in comparison - if it passes be sure to pick it
                    # - be sure to draw each obj in same way it was last drawn, esp if highlighted:
                    #    maybe drawn bigger (selatom)
                    #    moved towards screen
                    newpicked = self.check_target_depth(
                        obj, fudge, debug_prefix=debug_prefix)
                    # returns obj or None -- not sure if that should be guaranteed [bruce 050822 comment]
                    if newpicked is not None:
                        break
                except:
                    self.set_drawing_phase('?')
                    print_compact_traceback(
                        "exception in or near %r.draw_in_abs_coords ignored: "
                        % (obj, ))
        ##e should check depth here to make sure it's near enough but not too near
        # (if too near, it means objects moved, and we should cancel this pick)
        glClear(
            GL_DEPTH_BUFFER_BIT
        )  # prevent those predraws from messing up the subsequent main draws
        # REVIEW: is this slow? If so, it could be optimized by drawing them
        # into only one pixel and clearing only that pixel (see related code
        # in the glname-color code in do_glselect_if_wanted).
        # [bruce 090105 comment]
        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
        self.glselect_dict.clear(
        )  #k needed? even if not, seems safer this way.
        # do this now to avoid confusing the main draw methods,
        # in case they check this dict to decide whether they're
        # being called by draw_in_abs_coords
        # [which would be deprecated! but would work, not counting display lists.]
        #bruce 050822 new feature: objects which had no highlight color should not be allowed in self.selobj
        # (to make sure they can't be operated on without user intending this),
        # though they should still obscure other objects.

        if newpicked is not None:
            if debug_prefix and len(items) > 1:  #bruce 081209
                print "%s (%d): complete list of candidates were:" % (
                    debug_prefix, env.redraw_counter), items
            hicolor = self.selobj_hicolor(newpicked)
            if hicolor is None:
                if report_failures:
                    print "debug_pref: preDraw_glselect_dict failure: " \
                          "it worked, but %r hicolor is None, so discarding it" % \
                          (newpicked,) #bruce 081209
                newpicked = None
                # [note: there are one or two other checks of the same thing,
                #  e.g. to cover preexisting selobjs whose hicolor might have changed [bruce 060726 comment]]
        else:
            #bruce 060217 debug code re bug 1527. Not sure only happens on a bug, so using atom_debug.
            # (But I couldn't yet cause this to be printed while testing that bug.)
            #bruce 060224 disabling it since it's happening all the time when hover-highlighting in Build
            # (though I didn't reanalyze my reasons for thinking it might be a bug, so I don't know if it's a real one or not).
            #070115 changing condition from if 0 to a debug_pref, and revised message
            if report_failures:
                print "debug_pref: preDraw_glselect_dict failure: targetdepth is %r, items are %r" % (
                    self.targetdepth, items)
        ###e try printing it all -- is the candidate test just wrong?
        return newpicked  # might be None in case of errors (or if selobj_hicolor returns None)
Ejemplo n.º 9
0
    def _draw_highlighted_selobj(self, selobj, hicolor):
        #bruce 070920 split this out; 090311 renamed it
        """
        Draw selobj in highlighted form, using its "selobj drawing interface"
        (not yet a formal interface; we use several methods including draw_in_abs_coords).
        Record the drawn pixels in the OpenGL stencil buffer to optimize future
        detection of the mouse remaining over the same selobj (to avoid redraws then).
           Assume we have standard modelview and projection matrices on entry,
        and restore that state on exit (copying or recreating it as we prefer).
           Note: Current implementation uses an extra level on the projection matrix stack
        by default (selobj can override this). This could be easily revised if desired.
        """
        # draw the selobj as highlighted, and make provisions for fast test
        # (by external code) of mouse still being over it (using stencil buffer)

        # note: if selobj highlight is partly translucent or transparent (neither yet supported),
        # we might need to draw it depth-sorted with other translucent objects
        # (now drawn by some modes using Draw_after_highlighting, not depth-sorted or modularly);
        # but our use of the stencil buffer assumes it's drawn at the end (except for objects which
        # don't obscure it for purposes of highlighting hit-test). This will need to be thought through
        # carefully if there can be several translucent objects (meant to be opaque re hit-tests),
        # and traslucent highlighting. See also the comment about highlight_into_depth, below. [bruce 060724 comment]

        # first gather info needed to know what to do -- highlight color (and whether to draw that at all)
        # and whether object might be bigger when highlighted (affects whether depth write is needed now).

        assert hicolor is not None  #bruce 070919

        highlight_might_be_bigger = True  # True is always ok; someday we might let some objects tell us this can be False

        # color-writing is needed here, iff the mode asked for it, for this selobj.
        #
        # Note: in current code this is always True (as assertion above implies),
        # but it's possible we'll decide to retain self.selobj even if its
        # hicolor is None, but just not draw it in that case, or even to draw it
        # in some ways and not others -- so just in case, keep this test for now.
        # [bruce 070919 comment]
        highlight_into_color = (hicolor is not None)

        if highlight_into_color:
            # depth-writing is needed here, if highlight might be drawn in front of not-yet-drawn transparent surfaces
            # (like Build mode water surface) -- otherwise they will look like they're in front of some of the highlighting
            # even though they're not. (In principle, the preDraw_glselect_dict call above needs to know whether this depth
            # writing occurred ###doc why. Probably we should store it into the object itself... ###@@@ review, then doit )
            highlight_into_depth = highlight_might_be_bigger
        else:
            highlight_into_depth = False  ###@@@ might also need to store 0 into obj...see discussion above

        if not highlight_into_depth:
            glDepthMask(
                GL_FALSE)  # turn off depth writing (but not depth test)
        if not highlight_into_color:
            glColorMask(GL_FALSE, GL_FALSE, GL_FALSE,
                        GL_FALSE)  # don't draw color pixels

        # Note: stencil buffer was cleared earlier in this paintGL call.
        glStencilFunc(GL_ALWAYS, 1, 1)
        # These args make sure stencil test always passes, so color is drawn if we want it to be,
        # and so we can tell whether depth test passes in glStencilOp (even if depth *writing* is disabled ###untested);
        # this also sets reference value of 1 for use by GL_REPLACE.
        # (Args are: func to say when drawing-test passes; ref value; comparison mask.
        #  Warning: Passing -1 for reference value, to get all 1's, does not work -- it makes ref value 0!)
        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)
        # turn on stencil-buffer writing based on depth test
        # (args are: what to do on fail, zfail, zpass (see OpenGL "red book" p. 468))
        glEnable(GL_STENCIL_TEST)
        # this enables both aspects of the test: effect on drawing, and use of stencil op (I think #k);
        # apparently they can't be enabled separately
        ##print glGetIntegerv( GL_STENCIL_REF)

        # Now "translate the world" slightly closer to the screen,
        # to ensure depth test passes for appropriate parts of highlight-drawing
        # even if roundoff errors would make it unreliable to just let equal depths pass the test.
        # As of 070921 we use glDepthRange for this.

        self.setDepthRange_Highlighting()

        try:
            colorsorter_safe = getattr(selobj, '_selobj_colorsorter_safe',
                                       False)
            # colorsorter_safe being False (in two places in this file)
            # is a bugfix for the breaking of expr handle highlighting
            # by the debug_pref('GLPane: highlight atoms in CSDLs?')
            # (but bug's cause is not understood) [bruce 090311]
            self.set_drawing_phase('selobj')  #bruce 070124

            #bruce 070329 moved set of drawing_phase from just after selobj.draw_in_abs_coords to just before it.
            # [This should fix the Qt4 transition issue which is the subject of reminder bug 2300,
            #  though it can't be tested yet since it has no known effect on current code, only on future code.]
            def func():
                self.graphicsMode.Draw_highlighted_selobj(
                    self, selobj, hicolor)
                # TEST someday: test having color writing disabled here -- does stencil write still happen??
                # (not urgent, since we definitely need color writing here.)
                return

            self._call_func_that_draws_objects(
                func, self.part, bare_primitives=colorsorter_safe)
        except:
            # try/except added for GL-state safety, bruce 061218
            print_compact_traceback(
                "bug: exception in %r.Draw_highlighted_selobj for %r ignored: " % \
                (self.graphicsMode, selobj)
            )
            pass
        self.set_drawing_phase('?')

        self.setDepthRange_Normal()

        # restore other gl state
        # (but don't do unneeded OpenGL ops
        #  in case that speeds up OpenGL drawing)
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
        # no need to undo glStencilFunc state, I think -- whoever cares will set it up again
        # when they reenable stenciling.
        if not highlight_into_color:
            glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
        if not highlight_into_depth:
            glDepthMask(GL_TRUE)

        if debug_pref("GLPane: draw stencil buffer?",
                      Choice_boolean_False,
                      prefs_key=True):
            # draw stencil buffer in orange [bruce 090105]
            glStencilFunc(GL_EQUAL, 1, 1)  # only draw where stencil is set
            glDepthMask(GL_FALSE)
            glDisable(GL_DEPTH_TEST)
            # Note: according to some web forum (not confirmed in red book or by test),
            # glDisable(GL_DEPTH_TEST) also disables depth writing,
            # so the above glDepthMask(GL_FALSE) is redundant.
            # This differs from my recollection, so should be checked if it matters.
            # [bruce 090105]
            self.draw_solid_color_everywhere(orange)
            # note: we already drew highlighting selobj above, so that won't obscure this
            glEnable(GL_DEPTH_TEST)
            glDepthMask(GL_TRUE)
            pass

        glDisable(GL_STENCIL_TEST)

        return  # from _draw_highlighted_selobj
Ejemplo n.º 10
0
    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 _enable_stereo(self, stereo_image, preserve_colors = False, no_clipping = False):
        """
        Enables stereo rendering.

        This should be called before entering a drawing phase
        and should be accompanied by a self._disable_stereo call
        after the drawing for that phase.

        These methods push a modelview matrix on the matrix stack
        and modify the matrix.

        @param stereo_image: Indicates which stereo image is being drawn
                             (-1 == left, 1 == right, 0 == stereo is disabled)

        @param preserve_colors: Disable color mask manipulations,
                                which normally occur in anaglyph mode.
                                (Also disable depth buffer clearing
                                 for 2nd image, which occurs then.)

        @param no_clipping: Disable clipping, which normally occurs in
                            side-by-side mode.
        """
        if not self.stereo_enabled:
            return

        glPushAttrib(GL_TRANSFORM_BIT)

        stereo_mode = self.stereo_mode
        stereo_separation = self.stereo_separation
        stereo_angle = self.stereo_angle

        # push the modelview matrix on the stack
        glMatrixMode(GL_MODELVIEW)
        glPushMatrix()

        separation = stereo_separation * self.scale
        angle = stereo_angle # might be modified below

        if stereo_mode <= 2:
            # side-by-side stereo mode
            # clip left or right image
            # reset the modelview matrix

            if not no_clipping:
                glPushMatrix() # Note: this might look redundant with the
                    # glPushMatrix above, but removing it (and its glPopMatrix
                    # 10 lines below) causes a bug (no image visible).
                    # Guess: it's needed so the glLoadIdentity needed to set up
                    # the clipping plane just below has only a temporary effect.
                    # [bruce 080911 comment]
                glLoadIdentity()
                if stereo_image == -1:
                    clip_eq = (-1.0, 0.0, 0.0, 0.0)
                else:
                    clip_eq = ( 1.0, 0.0, 0.0, 0.0)
                # using GL_CLIP_PLANE5 for stereo clipping
                glClipPlane(GL_CLIP_PLANE5, clip_eq)
                glEnable(GL_CLIP_PLANE5)
                glPopMatrix()

            # for cross-eyed mode, exchange left and right views
            if stereo_mode == 2:
                angle *= -1

            # translate images for side-by-side stereo
            glTranslatef(separation * stereo_image * self.right[0],
                         separation * stereo_image * self.right[1],
                         separation * stereo_image * self.right[2])

            # rotate the stereo image ("toe-in" method)
            glRotatef(angle * stereo_image,
                      self.up[0],
                      self.up[1],
                      self.up[2])

        else:
            # Anaglyphs stereo mode - Red plus Blue, Cyan, or Green image.
            angle *= 0.5
            if not preserve_colors:
                if stereo_image == -1:
                    # red image
                    glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE)
                else:
                    # clear depth buffer to combine red/blue images
                    # REVIEW: this would cause bugs in the predraw_glselectdict code
                    # used for depth-testing selobj candidates. Maybe it does
                    # not happen then? The name of the flag 'preserve_colors'
                    # does not say anything about preserving depths.
                    # Anyway, it *does* happen then, so I think there is a bug.
                    # Also, remind me, what's the 4th arg to glColorMask?
                    # [bruce 080911 comment]
                    #
                    # piotr 080911 response: You are right. This technically
                    # causes a bug: the red image is not highlightable,
                    # but actually when using anaglyph glasses, the bug is not
                    # noticeable, as both red and blue images converge.
                    # The bug becomes noticeable if user tries to highlight an
                    # object without wearing the anaglyph glasses.
                    # At this point, I think we can either leave it as it is,
                    # or consider implementing anaglyph stereo by using
                    # OpenGL alpha blending.
                    # Also, I am not sure if highlighting problem is the only bug
                    # that is caused by clearing the GL_DEPTH_BUFFER_BIT.
                    glClear(GL_DEPTH_BUFFER_BIT)
                    if stereo_mode == 3:
                        # blue image
                        glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE)
                    elif stereo_mode == 4:
                        # cyan image
                        glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE)
                    elif stereo_mode == 5:
                        # green image
                        glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE)

            # rotate the stereo image ("toe-in" method)
            glRotatef(angle * stereo_image,
                      self.up[0],
                      self.up[1],
                      self.up[2])
        return
Ejemplo n.º 12
0
 def disable_color(self): ### really should be a glpane method
     "don't draw color pixels (but keep drawing depth pixels, if you were)"
     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
     pass
Ejemplo n.º 13
0
 def set_defaults(self, camera):
     camera.viewport_xrelstart = 0.0
     camera.viewport_relwidth = 1.0
     camera.eye_pos = 0.0 # middle eye        
     glColorMask(True, True, True, True)
Ejemplo n.º 14
0
 def enable_color(self):
     # nested ones would break, due to this in the inner one -- could be fixed by a counter, if we used them in matched pairs
     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
     pass
Ejemplo n.º 15
0
    def _draw_highlighted_selobj(self, selobj, hicolor):
        # bruce 070920 split this out; 090311 renamed it
        """
        Draw selobj in highlighted form, using its "selobj drawing interface"
        (not yet a formal interface; we use several methods including draw_in_abs_coords).
        Record the drawn pixels in the OpenGL stencil buffer to optimize future
        detection of the mouse remaining over the same selobj (to avoid redraws then).
           Assume we have standard modelview and projection matrices on entry,
        and restore that state on exit (copying or recreating it as we prefer).
           Note: Current implementation uses an extra level on the projection matrix stack
        by default (selobj can override this). This could be easily revised if desired.
        """
        # draw the selobj as highlighted, and make provisions for fast test
        # (by external code) of mouse still being over it (using stencil buffer)

        # note: if selobj highlight is partly translucent or transparent (neither yet supported),
        # we might need to draw it depth-sorted with other translucent objects
        # (now drawn by some modes using Draw_after_highlighting, not depth-sorted or modularly);
        # but our use of the stencil buffer assumes it's drawn at the end (except for objects which
        # don't obscure it for purposes of highlighting hit-test). This will need to be thought through
        # carefully if there can be several translucent objects (meant to be opaque re hit-tests),
        # and traslucent highlighting. See also the comment about highlight_into_depth, below. [bruce 060724 comment]

        # first gather info needed to know what to do -- highlight color (and whether to draw that at all)
        # and whether object might be bigger when highlighted (affects whether depth write is needed now).

        assert hicolor is not None  # bruce 070919

        highlight_might_be_bigger = (
            True
        )  # True is always ok; someday we might let some objects tell us this can be False

        # color-writing is needed here, iff the mode asked for it, for this selobj.
        #
        # Note: in current code this is always True (as assertion above implies),
        # but it's possible we'll decide to retain self.selobj even if its
        # hicolor is None, but just not draw it in that case, or even to draw it
        # in some ways and not others -- so just in case, keep this test for now.
        # [bruce 070919 comment]
        highlight_into_color = hicolor is not None

        if highlight_into_color:
            # depth-writing is needed here, if highlight might be drawn in front of not-yet-drawn transparent surfaces
            # (like Build mode water surface) -- otherwise they will look like they're in front of some of the highlighting
            # even though they're not. (In principle, the preDraw_glselect_dict call above needs to know whether this depth
            # writing occurred ###doc why. Probably we should store it into the object itself... ###@@@ review, then doit )
            highlight_into_depth = highlight_might_be_bigger
        else:
            highlight_into_depth = False  ###@@@ might also need to store 0 into obj...see discussion above

        if not highlight_into_depth:
            glDepthMask(GL_FALSE)  # turn off depth writing (but not depth test)
        if not highlight_into_color:
            glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)  # don't draw color pixels

        # Note: stencil buffer was cleared earlier in this paintGL call.
        glStencilFunc(GL_ALWAYS, 1, 1)
        # These args make sure stencil test always passes, so color is drawn if we want it to be,
        # and so we can tell whether depth test passes in glStencilOp (even if depth *writing* is disabled ###untested);
        # this also sets reference value of 1 for use by GL_REPLACE.
        # (Args are: func to say when drawing-test passes; ref value; comparison mask.
        #  Warning: Passing -1 for reference value, to get all 1's, does not work -- it makes ref value 0!)
        glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)
        # turn on stencil-buffer writing based on depth test
        # (args are: what to do on fail, zfail, zpass (see OpenGL "red book" p. 468))
        glEnable(GL_STENCIL_TEST)
        # this enables both aspects of the test: effect on drawing, and use of stencil op (I think #k);
        # apparently they can't be enabled separately
        ##print glGetIntegerv( GL_STENCIL_REF)

        # Now "translate the world" slightly closer to the screen,
        # to ensure depth test passes for appropriate parts of highlight-drawing
        # even if roundoff errors would make it unreliable to just let equal depths pass the test.
        # As of 070921 we use glDepthRange for this.

        self.setDepthRange_Highlighting()

        try:
            colorsorter_safe = getattr(selobj, "_selobj_colorsorter_safe", False)
            # colorsorter_safe being False (in two places in this file)
            # is a bugfix for the breaking of expr handle highlighting
            # by the debug_pref('GLPane: highlight atoms in CSDLs?')
            # (but bug's cause is not understood) [bruce 090311]
            self.set_drawing_phase("selobj")  # bruce 070124
            # bruce 070329 moved set of drawing_phase from just after selobj.draw_in_abs_coords to just before it.
            # [This should fix the Qt4 transition issue which is the subject of reminder bug 2300,
            #  though it can't be tested yet since it has no known effect on current code, only on future code.]
            def func():
                self.graphicsMode.Draw_highlighted_selobj(self, selobj, hicolor)
                # TEST someday: test having color writing disabled here -- does stencil write still happen??
                # (not urgent, since we definitely need color writing here.)
                return

            self._call_func_that_draws_objects(func, self.part, bare_primitives=colorsorter_safe)
        except:
            # try/except added for GL-state safety, bruce 061218
            print_compact_traceback(
                "bug: exception in %r.Draw_highlighted_selobj for %r ignored: " % (self.graphicsMode, selobj)
            )
            pass
        self.set_drawing_phase("?")

        self.setDepthRange_Normal()

        # restore other gl state
        # (but don't do unneeded OpenGL ops
        #  in case that speeds up OpenGL drawing)
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)
        # no need to undo glStencilFunc state, I think -- whoever cares will set it up again
        # when they reenable stenciling.
        if not highlight_into_color:
            glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
        if not highlight_into_depth:
            glDepthMask(GL_TRUE)

        if debug_pref("GLPane: draw stencil buffer?", Choice_boolean_False, prefs_key=True):
            # draw stencil buffer in orange [bruce 090105]
            glStencilFunc(GL_EQUAL, 1, 1)  # only draw where stencil is set
            glDepthMask(GL_FALSE)
            glDisable(GL_DEPTH_TEST)
            # Note: according to some web forum (not confirmed in red book or by test),
            # glDisable(GL_DEPTH_TEST) also disables depth writing,
            # so the above glDepthMask(GL_FALSE) is redundant.
            # This differs from my recollection, so should be checked if it matters.
            # [bruce 090105]
            self.draw_solid_color_everywhere(orange)
            # note: we already drew highlighting selobj above, so that won't obscure this
            glEnable(GL_DEPTH_TEST)
            glDepthMask(GL_TRUE)
            pass

        glDisable(GL_STENCIL_TEST)

        return  # from _draw_highlighted_selobj
Ejemplo n.º 16
0
    def _draw_saved_bg_image(self):
        """
        """
        print "_draw_saved_bg_image", self._print_data()
        sys.stdout.flush()
        
        assert self._cached_bg_color_image is not None

        w = _trim(self.width)
        h = _trim(self.height)

        glDisable(GL_DEPTH_TEST) # probably a speedup
        glDisable(GL_LIGHTING)
        glDisable(GL_TEXTURE_2D) # probably already the NE1 default (if so, doesn't matter here)        
            # Note: doing more disables might well speed up glDrawPixels;
            # don't know whether that matters.

        color_image = self._cached_bg_color_image
        depth_image = self._cached_bg_depth_image
        
        # draw the color image part (review: does this also modify the depth buffer?)
        self._set_raster_pos(0, 0)
        if 0 and 'kluge - draw depth as color':
            glDrawPixels(w, h, GL_RED, _GL_TYPE_FOR_DEPTH, depth_image)
        else:
            glDrawPixels(w, h, _GL_FORMAT_FOR_COLOR, GL_UNSIGNED_BYTE, color_image)
        
        # draw the depth image part; ###BUG: this seems to replace all colors with blue... fixed below
        self._set_raster_pos(0, 0) # adding this makes the all-gray bug slightly less bad

        glClear(GL_DEPTH_BUFFER_BIT) # should not matter, but see whether it does #####

        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) # don't draw color pixels -
            # fixes bug where this glDrawPixels replaced all colors with blue
            # (reference doc for glDrawPixels explains why - it makes fragments
            #  using current color and depths from image, draws them normally)
        print "depth_image[0][0] being written now:", depth_image[0][0] #####
            ## depth_image[0][0] being written now: 0.0632854178548

        ## glDrawPixels(w, h, GL_DEPTH_COMPONENT, _GL_TYPE_FOR_DEPTH, depth_image)
        # see if this works any better -- not sure which type to try:
        # types listed at http://pyopengl.sourceforge.net/documentation/ref/gl/drawpixels.html
        ## print glDrawPixels.__class__ ####
        glDrawPixelsf(GL_DEPTH_COMPONENT, depth_image) ## if it was PIL, could say .tostring("raw","R",0,-1))######
        
        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
        if 1:####
            from OpenGL.GL import glFlush
            glFlush()######
        self._set_raster_pos(0, 0) # precaution (untested)

        if 1: # confirm if possible
            print "readback of depth at 0,0:", glReadPixels( 0, 0, 1, 1, GL_DEPTH_COMPONENT, _GL_TYPE_FOR_DEPTH )[0][0] ######
                ## BUG: readback of depth at 0,0: 1.0
##            import Numeric
##            print "min depth:" , Numeric.minimum( glReadPixels( 0, 0, w, h, GL_DEPTH_COMPONENT, _GL_TYPE_FOR_DEPTH ) ) #### 6 args required
##            ## ValueError: invalid number of arguments
            print
        
        glEnable(GL_LIGHTING)
        glEnable(GL_DEPTH_TEST)
        # (but leave GL_TEXTURE_2D disabled)

        return
Ejemplo n.º 17
0
    def preDraw_glselect_dict(self):  # bruce 050609
        """
        Assume the depth buffer is clear.
        Draw each object in self.glselect_dict (in a certain order)
        (drawing them into depth buffer only) and measure its depth, 
        until you find one near (undeep) enough (at the target pixel)
        to account for the target depth (self.targetdepth). Return
        that one, but first clear the depth buffer again.
        
        @note: some details are not documented here; see the code.
        """
        # We need to draw glselect_dict objects separately,
        # so their drawing code runs now rather than in the past
        # (when some display list was being compiled),
        # so they can notice they're in that dict.
        # We also draw them first, so that the nearest one
        # (or one of them, if there's a tie)
        # is sure to update the depth buffer.
        # (Then we clear the depth buffer, so that this drawing
        #  doesn't mess up later drawing at the same depth.)
        # (If some mode with special drawing code wants to join this system,
        #  it should be refactored to store special nodes in the model
        #  which can be drawn in the standard way.)
        glMatrixMode(GL_MODELVIEW)
        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
        # optimization -- don't draw color pixels (depth is all we need)
        newpicked = None  # in case of errors, and to record found object
        # here we should sort the objs to check the ones we most want first
        # (especially selobj)...
        # bruce 050702 try sorting this, see if it helps pick bonds rather than
        # invisible selatoms -- it seems to help.
        # This removes a bad side effect of today's earlier fix of bug 715-1.
        objects = self.glselect_dict.values()
        items = []  # (order, obj) pairs, for sorting objects
        for obj in objects:
            ### MAYBE TODO: add a special order for the object found previously
            # by the glname-to-color code for shader primitives.
            # But only do this after we're sure we draw all other objects
            # then too. And its order should be worse than selobj, I think.
            # [bruce 090105 comment]
            if obj is self.selobj:
                order = 0
            elif isinstance(obj, Bond):
                # bruce 080402 precaution: don't say obj.__class__ is Bond,
                # in case obj has no __class__
                order = 1
            else:
                order = 2
            order = (order, id(obj))
            # bruce 080402 work around bug in Bond.__eq__ for bonds not on
            # the same atom; later on 080402 I fixed that bug, but I'll
            # leave this for safety in case of __eq__ bugs on other kinds
            # of selobjs (e.g. dependence on other.__class__)
            items.append((order, obj))
        items.sort()
        report_failures = debug_pref(
            "GLPane: preDraw_glselect_dict: report failures?", Choice_boolean_False, prefs_key=True
        )
        if debug_pref("GLPane: check_target_depth debug prints?", Choice_boolean_False, prefs_key=True):
            debug_prefix = "check_target_depth"
        else:
            debug_prefix = None
        fudge = self.graphicsMode.check_target_depth_fudge_factor  # bruce 070115 kluge for testmode
        ### REVIEW: should this be an attribute of each object which can be drawn as selobj, instead?
        # The reasons it's needed are the same ones that require a nonzero DEPTH_TWEAK in GLPane_minimal.
        # See also the comment about it inside check_target_depth. [bruce 070921 comment]
        for orderjunk, obj in items:  # iterate over candidates
            try:
                method = obj.draw_in_abs_coords
            except AttributeError:
                print "bug? ignored: %r has no draw_in_abs_coords method" % (obj,)
                print "   items are:", items
            else:
                try:
                    colorsorter_safe = getattr(obj, "_selobj_colorsorter_safe", False)
                    # colorsorter_safe being False (in two places in this file)
                    # is a bugfix for the breaking of expr handle highlighting
                    # by the debug_pref('GLPane: highlight atoms in CSDLs?')
                    # (but bug's cause is not understood) [bruce 090311]
                    for stereo_image in self.stereo_images_to_draw:
                        # REVIEW: would it be more efficient, and correct,
                        # to iterate over stereo images outside, and candidates
                        # inside (i.e. turn this pair of loops inside out)?
                        # I guess that would require knowing which stereo_image
                        # we're sampling in... and in that case we'd want to use
                        # only one of them anyway to do the testing
                        # (probably even if they overlap, just pick one and
                        # use that one -- see related comments in _enable_stereo).
                        # [bruce 080911 comment]
                        self._enable_stereo(stereo_image)

                        self.set_drawing_phase("selobj/preDraw_glselect_dict")  # bruce 070124

                        def func():
                            method(self, white)
                            # draw depth info
                            # (color doesn't matter since we're not drawing pixels)
                            return

                        self._call_func_that_draws_objects(func, self.part, bare_primitives=colorsorter_safe)

                        self._disable_stereo()

                    self.set_drawing_phase("?")
                    # bruce 050822 changed black to white in case some draw methods have boolean-test bugs for black (unlikely)
                    ###@@@ in principle, this needs bugfixes; in practice the bugs are tolerable in the short term
                    # (see longer discussion in other comments):
                    # - if no one reaches target depth, or more than one does, be smarter about what to do?
                    # - try current selobj first [this is done, as of 050702],
                    #   or give it priority in comparison - if it passes be sure to pick it
                    # - be sure to draw each obj in same way it was last drawn, esp if highlighted:
                    #    maybe drawn bigger (selatom)
                    #    moved towards screen
                    newpicked = self.check_target_depth(obj, fudge, debug_prefix=debug_prefix)
                    # returns obj or None -- not sure if that should be guaranteed [bruce 050822 comment]
                    if newpicked is not None:
                        break
                except:
                    self.set_drawing_phase("?")
                    print_compact_traceback("exception in or near %r.draw_in_abs_coords ignored: " % (obj,))
        ##e should check depth here to make sure it's near enough but not too near
        # (if too near, it means objects moved, and we should cancel this pick)
        glClear(GL_DEPTH_BUFFER_BIT)  # prevent those predraws from messing up the subsequent main draws
        # REVIEW: is this slow? If so, it could be optimized by drawing them
        # into only one pixel and clearing only that pixel (see related code
        # in the glname-color code in do_glselect_if_wanted).
        # [bruce 090105 comment]
        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
        self.glselect_dict.clear()  # k needed? even if not, seems safer this way.
        # do this now to avoid confusing the main draw methods,
        # in case they check this dict to decide whether they're
        # being called by draw_in_abs_coords
        # [which would be deprecated! but would work, not counting display lists.]
        # bruce 050822 new feature: objects which had no highlight color should not be allowed in self.selobj
        # (to make sure they can't be operated on without user intending this),
        # though they should still obscure other objects.

        if newpicked is not None:
            if debug_prefix and len(items) > 1:  # bruce 081209
                print "%s (%d): complete list of candidates were:" % (debug_prefix, env.redraw_counter), items
            hicolor = self.selobj_hicolor(newpicked)
            if hicolor is None:
                if report_failures:
                    print "debug_pref: preDraw_glselect_dict failure: " "it worked, but %r hicolor is None, so discarding it" % (
                        newpicked,
                    )  # bruce 081209
                newpicked = None
                # [note: there are one or two other checks of the same thing,
                #  e.g. to cover preexisting selobjs whose hicolor might have changed [bruce 060726 comment]]
        else:
            # bruce 060217 debug code re bug 1527. Not sure only happens on a bug, so using atom_debug.
            # (But I couldn't yet cause this to be printed while testing that bug.)
            # bruce 060224 disabling it since it's happening all the time when hover-highlighting in Build
            # (though I didn't reanalyze my reasons for thinking it might be a bug, so I don't know if it's a real one or not).
            # 070115 changing condition from if 0 to a debug_pref, and revised message
            if report_failures:
                print "debug_pref: preDraw_glselect_dict failure: targetdepth is %r, items are %r" % (
                    self.targetdepth,
                    items,
                )
        ###e try printing it all -- is the candidate test just wrong?
        return newpicked  # might be None in case of errors (or if selobj_hicolor returns None)
Ejemplo n.º 18
0
 def enable_color(self):
     # nested ones would break, due to this in the inner one -- could be fixed by a counter, if we used them in matched pairs
     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
     pass
Ejemplo n.º 19
0
    def _enable_stereo(self,
                       stereo_image,
                       preserve_colors=False,
                       no_clipping=False):
        """
        Enables stereo rendering.

        This should be called before entering a drawing phase
        and should be accompanied by a self._disable_stereo call
        after the drawing for that phase.

        These methods push a modelview matrix on the matrix stack
        and modify the matrix.

        @param stereo_image: Indicates which stereo image is being drawn
                             (-1 == left, 1 == right, 0 == stereo is disabled)

        @param preserve_colors: Disable color mask manipulations,
                                which normally occur in anaglyph mode.
                                (Also disable depth buffer clearing
                                 for 2nd image, which occurs then.)

        @param no_clipping: Disable clipping, which normally occurs in
                            side-by-side mode.
        """
        if not self.stereo_enabled:
            return

        glPushAttrib(GL_TRANSFORM_BIT)

        stereo_mode = self.stereo_mode
        stereo_separation = self.stereo_separation
        stereo_angle = self.stereo_angle

        # push the modelview matrix on the stack
        glMatrixMode(GL_MODELVIEW)
        glPushMatrix()

        separation = stereo_separation * self.scale
        angle = stereo_angle  # might be modified below

        if stereo_mode <= 2:
            # side-by-side stereo mode
            # clip left or right image
            # reset the modelview matrix

            if not no_clipping:
                glPushMatrix()  # Note: this might look redundant with the
                # glPushMatrix above, but removing it (and its glPopMatrix
                # 10 lines below) causes a bug (no image visible).
                # Guess: it's needed so the glLoadIdentity needed to set up
                # the clipping plane just below has only a temporary effect.
                # [bruce 080911 comment]
                glLoadIdentity()
                if stereo_image == -1:
                    clip_eq = (-1.0, 0.0, 0.0, 0.0)
                else:
                    clip_eq = (1.0, 0.0, 0.0, 0.0)
                # using GL_CLIP_PLANE5 for stereo clipping
                glClipPlane(GL_CLIP_PLANE5, clip_eq)
                glEnable(GL_CLIP_PLANE5)
                glPopMatrix()

            # for cross-eyed mode, exchange left and right views
            if stereo_mode == 2:
                angle *= -1

            # translate images for side-by-side stereo
            glTranslatef(separation * stereo_image * self.right[0],
                         separation * stereo_image * self.right[1],
                         separation * stereo_image * self.right[2])

            # rotate the stereo image ("toe-in" method)
            glRotatef(angle * stereo_image, self.up[0], self.up[1], self.up[2])

        else:
            # Anaglyphs stereo mode - Red plus Blue, Cyan, or Green image.
            angle *= 0.5
            if not preserve_colors:
                if stereo_image == -1:
                    # red image
                    glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE)
                else:
                    # clear depth buffer to combine red/blue images
                    # REVIEW: this would cause bugs in the predraw_glselectdict code
                    # used for depth-testing selobj candidates. Maybe it does
                    # not happen then? The name of the flag 'preserve_colors'
                    # does not say anything about preserving depths.
                    # Anyway, it *does* happen then, so I think there is a bug.
                    # Also, remind me, what's the 4th arg to glColorMask?
                    # [bruce 080911 comment]
                    #
                    # piotr 080911 response: You are right. This technically
                    # causes a bug: the red image is not highlightable,
                    # but actually when using anaglyph glasses, the bug is not
                    # noticeable, as both red and blue images converge.
                    # The bug becomes noticeable if user tries to highlight an
                    # object without wearing the anaglyph glasses.
                    # At this point, I think we can either leave it as it is,
                    # or consider implementing anaglyph stereo by using
                    # OpenGL alpha blending.
                    # Also, I am not sure if highlighting problem is the only bug
                    # that is caused by clearing the GL_DEPTH_BUFFER_BIT.
                    glClear(GL_DEPTH_BUFFER_BIT)
                    if stereo_mode == 3:
                        # blue image
                        glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE)
                    elif stereo_mode == 4:
                        # cyan image
                        glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE)
                    elif stereo_mode == 5:
                        # green image
                        glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE)

            # rotate the stereo image ("toe-in" method)
            glRotatef(angle * stereo_image, self.up[0], self.up[1], self.up[2])
        return