Example #1
0
    def draw3D(self, *args, **kwargs):
        """Calls the version dependent ``draw3D`` function. """

        opts = self.opts
        w, h = self.canvas.GetSize()
        res = self.opts.resolution / 100.0
        w = int(np.ceil(w * res))
        h = int(np.ceil(h * res))

        # Initialise and resize
        # the offscreen textures
        for rt in [self.renderTexture1, self.renderTexture2]:
            if rt.getSize() != (w, h):
                rt.setSize(w, h)

            rt.bindAsRenderTarget()
            gl.glClearColor(0, 0, 0, 0)
            gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
            rt.unbindAsRenderTarget()

        if opts.resolution != 100:
            gl.glViewport(0, 0, w, h)

        # Do the render. Even though we're
        # drawing off-screen,  we need to
        # enable depth-testing, otherwise
        # depth values will not get written
        # to the depth buffer!
        with glroutines.enabled((gl.GL_DEPTH_TEST, gl.GL_CULL_FACE)):
            gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL)
            gl.glFrontFace(gl.GL_CCW)
            gl.glCullFace(gl.GL_BACK)
            fslgl.glvolume_funcs.draw3D(self, *args, **kwargs)

        # renderTexture1 should now
        # contain the final result -
        # draw it to the screen.
        verts = np.array([[-1, -1, 0], [-1, 1, 0], [1, -1, 0], [1, -1, 0],
                          [-1, 1, 0], [1, 1, 0]],
                         dtype=np.float32)

        invproj = transform.invert(self.canvas.getProjectionMatrix())
        verts = transform.transform(verts, invproj)

        if opts.resolution != 100:
            w, h = self.canvas.GetSize()
            gl.glViewport(0, 0, w, h)

        with glroutines.enabled((gl.GL_DEPTH_TEST)):
            self.renderTexture1.draw(verts, useDepth=True)
Example #2
0
def drawAll(self, axes, zposes, xforms):
    """Draws mutltiple slices of the given image at the given Z position,
    applying the corresponding transformation to each of the slices.
    """

    nslices = len(zposes)
    vertices = np.zeros((nslices * 6, 3), dtype=np.float32)
    texCoords = np.zeros((nslices * 6, 3), dtype=np.float32)
    indices = np.arange(nslices * 6, dtype=np.uint32)

    for i, (zpos, xform) in enumerate(zip(zposes, xforms)):

        v, vc, tc = self.generateVertices2D(zpos, axes)

        vertices[i * 6:i * 6 + 6, :] = affine.transform(v, xform)
        texCoords[i * 6:i * 6 + 6, :] = tc

    vertices = vertices.ravel('C')

    self.shader.setAtt('texCoord', texCoords)

    with glroutines.enabled((gl.GL_VERTEX_ARRAY)):
        gl.glVertexPointer(3, gl.GL_FLOAT, 0, vertices)
        gl.glDrawElements(gl.GL_TRIANGLES, nslices * 6, gl.GL_UNSIGNED_INT,
                          indices)
Example #3
0
 def draw2D(self, zpos, axes, xform=None, bbox=None):
     """Calls :func:`.glrgbvolume_funcs.draw2D`. """
     with glroutines.enabled((gl.GL_CULL_FACE)):
         gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL)
         gl.glCullFace(gl.GL_BACK)
         gl.glFrontFace(self.frontFace())
         fslgl.glrgbvolume_funcs.draw2D(self, zpos, axes, xform, bbox)
Example #4
0
    def draw2D(self, zpos, axes):
        """Draws this ``VoxelSelection``."""

        xax, yax     = axes[:2]
        opts         = self.__opts
        texture      = self.__texture
        shape        = self.__selection.getSelection().shape
        displayToVox = opts.getTransform('display', 'voxel')
        voxToDisplay = opts.getTransform('voxel',   'display')
        voxToTex     = opts.getTransform('voxel',   'texture')
        voxToTex     = affine.concat(texture.texCoordXform(shape), voxToTex)
        verts, voxs  = glroutines.slice2D(shape,
                                          xax,
                                          yax,
                                          zpos,
                                          voxToDisplay,
                                          displayToVox)

        texs  = affine.transform(voxs, voxToTex)[:, :texture.ndim]
        verts = np.array(verts, dtype=np.float32).ravel('C')
        texs  = np.array(texs,  dtype=np.float32).ravel('C')

        texture.bindTexture(gl.GL_TEXTURE0)
        gl.glClientActiveTexture(gl.GL_TEXTURE0)
        gl.glTexEnvf(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_MODULATE)

        with glroutines.enabled((texture.target,
                                 gl.GL_TEXTURE_COORD_ARRAY,
                                 gl.GL_VERTEX_ARRAY)):
            gl.glVertexPointer(  3,            gl.GL_FLOAT, 0, verts)
            gl.glTexCoordPointer(texture.ndim, gl.GL_FLOAT, 0, texs)
            gl.glDrawArrays(     gl.GL_TRIANGLES, 0, 6)

        texture.unbindTexture()
Example #5
0
    def draw2D(self, *args, **kwargs):
        """Calls the version dependent ``draw2D`` function. """

        with glroutines.enabled((gl.GL_CULL_FACE)):
            gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL)
            gl.glCullFace(gl.GL_BACK)
            gl.glFrontFace(self.frontFace())
            fslgl.glvolume_funcs.draw2D(self, *args, **kwargs)
Example #6
0
 def draw2D(self, *args, **kwargs):
     """Overrides :meth:`.GLVector.draw2D`. Calls the OpenGL
     version-specific ``draw2D`` function.
     """
     with glroutines.enabled((gl.GL_CULL_FACE)):
         gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL)
         gl.glCullFace(gl.GL_BACK)
         gl.glFrontFace(self.frontFace())
         fslgl.glrgbvector_funcs.draw2D(self, *args, **kwargs)
Example #7
0
def draw(self,
         glType,
         vertices,
         indices=None,
         normals=None,
         vdata=None,
         mdata=None):
    """Called for 3D meshes, and :attr:`.MeshOpts.vertexData` is not
    ``None``. Loads and runs the shader program.

    :arg glType:   The OpenGL primitive type.

    :arg vertices: ``(n, 3)`` array containing the line vertices to draw.

    :arg indices:  Indices into the ``vertices`` array. If not provided,
                   ``glDrawArrays`` is used.

    :arg normals:  Vertex normals.

    :arg vdata:    ``(n, )`` array containing data for each vertex.

    :arg mdata:    ``(n, )`` array containing alpha modulation data for
                   each vertex.
    """

    shader = self.activeShader

    if normals is not None: shader.setAtt('normal',       normals)
    if vdata   is not None: shader.setAtt('vertexData',   vdata.reshape(-1, 1))
    if mdata   is not None: shader.setAtt('modulateData', mdata.reshape(-1, 1))

    if normals is not None:

        # NOTE You are assuming here that the canvas
        #      view matrix is the GL model view matrix.
        normalMatrix = self.canvas.viewMatrix
        normalMatrix = affine.invert(normalMatrix).T

        shader.setVertParam('normalMatrix', normalMatrix)

    shader.loadAtts()

    nvertices = vertices.shape[0]
    vertices  = vertices.ravel('C')

    with glroutines.enabled((gl.GL_VERTEX_ARRAY)):
        gl.glVertexPointer(3, gl.GL_FLOAT, 0, vertices)

        if indices is None:
            gl.glDrawArrays(glType, 0, nvertices)
        else:
            gl.glDrawElements(glType,
                              indices.shape[0],
                              gl.GL_UNSIGNED_INT,
                              indices.ravel('C'))
Example #8
0
    def _draw(self):
        """Draws the scene to the canvas. """

        if not self._setGLContext():
            return

        opts = self.opts
        glroutines.clear(opts.bgColour)

        if not self.__setViewport():
            return

        overlays, globjs = self.getGLObjects()

        if len(overlays) == 0:
            return

        # If occlusion is on, we offset the
        # depth of each overlay so that, where
        # a depth collision occurs, overlays
        # which are higher in the list will get
        # drawn above (closer to the screen)
        # than lower ones.
        depthOffset = transform.scaleOffsetXform(1, [0, 0, 0.1])
        depthOffset = np.array(depthOffset, dtype=np.float32, copy=False)
        xform = np.array(self.__viewMat, dtype=np.float32, copy=False)

        for ovl, globj in zip(overlays, globjs):

            display = self.__displayCtx.getDisplay(ovl)

            if not globj.ready():
                continue
            if not display.enabled:
                continue

            if opts.occlusion:
                xform = transform.concat(depthOffset, xform)
            elif isinstance(ovl, fslimage.Image):
                gl.glClear(gl.GL_DEPTH_BUFFER_BIT)

            log.debug('Drawing {} [{}]'.format(ovl, globj))

            globj.preDraw(xform=xform)
            globj.draw3D(xform=xform)
            globj.postDraw(xform=xform)

        if opts.showCursor:
            with glroutines.enabled((gl.GL_DEPTH_TEST)):
                self.__drawCursor()

        if opts.showLegend:
            self.__drawLegend()
Example #9
0
    def draw3D(self, xform=None, bbox=None):
        """Overrides :meth:`.GLObject.draw3D`. Draws a 3D rendering of the
        mesh.
        """
        opts      = self.opts
        verts     = self.vertices
        idxs      = self.indices
        normals   = self.normals
        blo, bhi  = self.getDisplayBounds()
        vdata     = opts.getVertexData('vertex')
        mdata     = opts.getVertexData('modulate')

        if mdata is None:
            mdata = vdata

        # TODO separate modulateDataIndex?
        if vdata is not None: vdata = vdata[:, self.opts.vertexDataIndex]
        if mdata is not None: mdata = mdata[:, 0]

        is2D = np.isclose(bhi[2], blo[2])

        if opts.wireframe:
            gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_LINE)
            gl.glLineWidth(2)
        else:
            gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL)

        if xform is not None:
            gl.glMatrixMode(gl.GL_MODELVIEW)
            gl.glPushMatrix()
            gl.glMultMatrixf(np.array(xform, dtype=np.float32).ravel('F'))

        if is2D or opts.wireframe:
            enable = (gl.GL_DEPTH_TEST)
        else:
            enable = (gl.GL_DEPTH_TEST, gl.GL_CULL_FACE)

        gl.glDisable(gl.GL_CULL_FACE)
        with glroutines.enabled(enable):
            gl.glFrontFace(self.frontFace())
            if not is2D:
                gl.glCullFace(gl.GL_BACK)
            fslgl.glmesh_funcs.draw(
                self,
                gl.GL_TRIANGLES,
                verts,
                normals=normals,
                indices=idxs,
                vdata=vdata,
                mdata=mdata)

        if xform is not None:
            gl.glPopMatrix()
Example #10
0
    def draw(self,
             vertices=None,
             xform=None,
             textureUnit=None):
        """Draw the contents of this ``Texture2D`` to a region specified by
        the given vertices. The texture is bound to texture unit 0.

        :arg vertices:    A ``numpy`` array of shape ``6 * 3`` specifying the
                          region, made up of two triangles, to which this
                          ``Texture2D`` should be drawn. If ``None``, it is
                          assumed that the vertices and texture coordinates
                          have already been configured (e.g. via a shader
                          program).

        :arg xform:       A transformation to be applied to the vertices.
                          Ignored if ``vertices is None``.

        :arg textureUnit: Texture unit to bind to. Defaults to
                          ``gl.GL_TEXTURE0``.
        """

        if textureUnit is None:
            textureUnit = gl.GL_TEXTURE0

        vertices, texCoords, indices = self.__prepareCoords(vertices, xform)

        with self.bound(textureUnit):
            gl.glClientActiveTexture(textureUnit)
            gl.glTexEnvf(gl.GL_TEXTURE_ENV,
                         gl.GL_TEXTURE_ENV_MODE,
                         gl.GL_REPLACE)

            glfeatures = [gl.GL_TEXTURE_2D, gl.GL_VERTEX_ARRAY]

            # Only enable texture coordinates if we know
            # that there are texture coordinates. Some GL
            # platforms will crash if texcoords are
            # enabled on a texture unit, but no texcoords
            # are loaded.
            if vertices is not None:
                glfeatures.append(gl.GL_TEXTURE_COORD_ARRAY)

            with glroutines.enabled(glfeatures):

                if vertices is not None:
                    gl.glVertexPointer(  3, gl.GL_FLOAT, 0, vertices)
                    gl.glTexCoordPointer(2, gl.GL_FLOAT, 0, texCoords)

                gl.glDrawElements(gl.GL_TRIANGLES, 6, gl.GL_UNSIGNED_INT,
                                  indices)
Example #11
0
    def drawClipPlanes(self, xform=None, bbox=None):
        """A convenience method for use with overlays being displayed
        in terms of a :class:`.Volume3DOpts` instance.

        Draws the active clipping planes, as specified by the
        :class:`.Volume3DOpts` clipping properties.

        :arg xform: A transformation matrix to apply to the clip plane
                    vertices before drawing them.

        :arg bbox:  A bounding box by which the clip planes can be limited
                    (not currently honoured).
        """

        if not self.opts.showClipPlanes:
            return

        for i in range(self.opts.numClipPlanes):

            verts, idxs = self.clipPlaneVertices(i, bbox)

            if len(idxs) == 0:
                continue

            if xform is not None:
                verts = transform.transform(verts, xform)

            verts = np.array(verts.ravel('C'), dtype=np.float32, copy=False)

            # A consistent colour for
            # each clipping plane
            rgb = self.__clipPlaneColours.get(i, None)
            if rgb is None:
                rgb = fslcmaps.randomBrightColour()[:3]
                self.__clipPlaneColours[i] = rgb

            r, g, b = rgb

            with glroutines.enabled(gl.GL_VERTEX_ARRAY):

                gl.glColor4f(r, g, b, 0.3)
                gl.glVertexPointer(3, gl.GL_FLOAT, 0, verts)
                gl.glDrawElements(gl.GL_TRIANGLES,
                                  len(idxs),
                                  gl.GL_UNSIGNED_INT,
                                  idxs)
Example #12
0
    def draw(self, vertices, xform=None):
        """Draw the contents of this ``Texture2D`` to a region specified by
        the given vertices. The texture is bound to texture unit 0.

        :arg vertices: A ``numpy`` array of shape ``6 * 3`` specifying the
                       region, made up of two triangles, to which this
                       ``Texture2D`` should be drawn.

        :arg xform:    A transformation to be applied to the vertices.
        """

        if vertices.shape != (6, 3):
            raise ValueError('Six vertices must be provided')

        if xform is not None:
            vertices = transform.transform(vertices, xform)

        vertices = np.array(vertices, dtype=np.float32)
        texCoords = np.zeros((6, 2), dtype=np.float32)
        indices = np.arange(6, dtype=np.uint32)

        texCoords[0, :] = [0, 0]
        texCoords[1, :] = [0, 1]
        texCoords[2, :] = [1, 0]
        texCoords[3, :] = [1, 0]
        texCoords[4, :] = [0, 1]
        texCoords[5, :] = [1, 1]

        vertices = vertices.ravel('C')
        texCoords = texCoords.ravel('C')

        self.bindTexture(gl.GL_TEXTURE0)

        gl.glClientActiveTexture(gl.GL_TEXTURE0)

        gl.glTexEnvf(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_REPLACE)

        with glroutines.enabled(
            (gl.GL_TEXTURE_2D, gl.GL_TEXTURE_COORD_ARRAY, gl.GL_VERTEX_ARRAY)):

            gl.glVertexPointer(3, gl.GL_FLOAT, 0, vertices)
            gl.glTexCoordPointer(2, gl.GL_FLOAT, 0, texCoords)
            gl.glDrawElements(gl.GL_TRIANGLES, 6, gl.GL_UNSIGNED_INT, indices)

        self.unbindTexture()
Example #13
0
def draw2D(self, zpos, axes, xform=None, bbox=None):
    """Draws a 2D slice of the image at the given Z location. """

    vertices, voxCoords, texCoords = self.generateVertices2D(
        zpos, axes, bbox=bbox)

    if xform is not None:
        vertices = transform.transform(vertices, xform)

    vertices = np.array(vertices, dtype=np.float32).ravel('C')

    # Voxel coordinates are calculated
    # in the vertex program
    self.shader.setAtt('texCoord', texCoords)

    with glroutines.enabled((gl.GL_VERTEX_ARRAY)):
        gl.glVertexPointer(3, gl.GL_FLOAT, 0, vertices)
        gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
Example #14
0
def draw3D(self, xform=None, bbox=None):
    """Draws the image in 3D on the canvas.

    :arg self:    The :class:`.GLVolume` object which is managing the image
                  to be drawn.

    :arg xform:   A 4*4 transformation matrix to be applied to the vertex
                  data.

    :arg bbox:    An optional bounding box.
    """
    opts = self.opts
    canvas = self.canvas
    display = self.display
    shader = self.shader
    shape = self.image.shape
    proj = canvas.projectionMatrix
    src = self.renderTexture1
    dest = self.renderTexture2
    w, h = src.shape

    vertices, voxCoords, texCoords = self.generateVertices3D(bbox)
    rayStep, texform = opts.calculateRayCastSettings(xform, proj)

    rayStep = affine.transformNormal(rayStep,
                                     self.imageTexture.texCoordXform(shape))
    texform = affine.concat(texform, self.imageTexture.invTexCoordXform(shape))

    if xform is not None:
        vertices = affine.transform(vertices, xform)

    vertices = np.array(vertices, dtype=np.float32).ravel('C')

    outerLoop = opts.getNumOuterSteps()
    screenSize = [1.0 / w, 1.0 / h, 0, 0]
    rayStep = list(rayStep) + [0]
    texform = texform[2, :]
    settings = [(1 - opts.blendFactor)**2, 0, 0, display.alpha / 100.0]

    gl.glVertexPointer(3, gl.GL_FLOAT, 0, vertices)

    shader.setAtt('texCoord', texCoords)
    shader.setFragParam('rayStep', rayStep)
    shader.setFragParam('screenSize', screenSize)
    shader.setFragParam('tex2ScreenXform', texform)

    # Disable blending - we want each
    # loop to replace the contents of
    # the texture, not blend into it!
    with glroutines.enabled((gl.GL_VERTEX_ARRAY)), \
         glroutines.disabled((gl.GL_BLEND)):

        for i in range(outerLoop):

            settings = list(settings)
            dtex = src.depthTexture
            settings[1] = i * opts.numInnerSteps

            if i == outerLoop - 1: settings[2] = 1
            else: settings[2] = -1

            shader.setFragParam('settings', settings)

            dest.bindAsRenderTarget()
            src.bindTexture(gl.GL_TEXTURE5)
            dtex.bindTexture(gl.GL_TEXTURE6)

            gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
            gl.glDrawArrays(gl.GL_TRIANGLES, 0, 36)

            src.unbindTexture()
            dtex.unbindTexture()
            dest.unbindAsRenderTarget()

            dest, src = src, dest

    shader.unloadAtts()
    shader.unload()

    self.renderTexture1 = src
    self.renderTexture2 = dest
Example #15
0
    def draw3D(self, *args, **kwargs):
        """Calls the version dependent ``draw3D`` function. """

        opts = self.opts
        w, h = self.canvas.GetScaledSize()
        res = self.opts.resolution / 100.0
        sw = int(np.ceil(w * res))
        sh = int(np.ceil(h * res))

        # Initialise and resize
        # the offscreen textures
        for rt in [self.renderTexture1, self.renderTexture2]:
            if rt.getSize() != (sw, sh):
                rt.setSize(sw, sh)

            with rt.bound():
                gl.glClearColor(0, 0, 0, 0)
                gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)

        if opts.resolution != 100:
            gl.glViewport(0, 0, sw, sh)

        # Do the render. Even though we're
        # drawing off-screen,  we need to
        # enable depth-testing, otherwise
        # depth values will not get written
        # to the depth buffer!
        #
        # The glvolume_funcs.draw3D function
        # will put the final render into
        # renderTexture1
        with glroutines.enabled((gl.GL_DEPTH_TEST, gl.GL_CULL_FACE)):
            gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL)
            gl.glFrontFace(gl.GL_CCW)
            gl.glCullFace(gl.GL_BACK)
            fslgl.glvolume_funcs.draw3D(self, *args, **kwargs)

        # Apply smoothing if needed. If smoothing
        # is enabled, the final final render will
        # be in renderTexture2
        if opts.smoothing > 0:
            self.smoothFilter.set(offsets=[1.0 / sw, 1.0 / sh])
            self.smoothFilter.osApply(self.renderTexture1, self.renderTexture2)

        # We now have the final result
        # - draw it to the screen.
        verts = np.array([[-1, -1, 0], [-1, 1, 0], [1, -1, 0], [1, -1, 0],
                          [-1, 1, 0], [1, 1, 0]],
                         dtype=np.float32)

        invproj = transform.invert(self.canvas.projectionMatrix)
        verts = transform.transform(verts, invproj)

        if opts.resolution != 100:
            gl.glViewport(0, 0, w, h)

        with glroutines.enabled(gl.GL_DEPTH_TEST):

            # If smoothing was not applied, rt1
            # contains the final render. Otherwise,
            # rt2 contains the final render, but rt1
            # contains the depth information. So we
            # need to # temporarily replace rt2.depth
            # with rt1.depth.
            if opts.smoothing > 0:
                src = self.renderTexture2
                olddep = self.renderTexture2.depthTexture
                dep = self.renderTexture1.depthTexture
            else:
                src = self.renderTexture1
                olddep = self.renderTexture1.depthTexture
                dep = olddep

            src.depthTexture = dep
            src.draw(verts, useDepth=True)
            src.depthTexture = olddep
Example #16
0
    def _draw(self):
        """Draws the scene to the canvas. """

        if self.destroyed:
            return

        if not self._setGLContext():
            return

        opts = self.opts
        glroutines.clear(opts.bgColour)

        if not self.__setViewport():
            return

        overlays, globjs = self.getGLObjects()

        if len(overlays) == 0:
            return

        # If occlusion is on, we offset the
        # depth of each overlay so that, where
        # a depth collision occurs, overlays
        # which are higher in the list will get
        # drawn above (closer to the screen)
        # than lower ones.
        depthOffset = affine.scaleOffsetXform(1, [0, 0, 0.1])
        depthOffset = np.array(depthOffset, dtype=np.float32, copy=False)
        xform = np.array(self.__viewMat, dtype=np.float32, copy=False)

        for ovl, globj in zip(overlays, globjs):

            display = self.__displayCtx.getDisplay(ovl)

            if not globj.ready():
                continue
            if not display.enabled:
                continue

            if opts.occlusion:
                xform = affine.concat(depthOffset, xform)
            elif isinstance(ovl, fslimage.Image):
                gl.glClear(gl.GL_DEPTH_BUFFER_BIT)

            log.debug('Drawing {} [{}]'.format(ovl, globj))

            globj.preDraw(xform=xform)
            globj.draw3D(xform=xform)
            globj.postDraw(xform=xform)

        if opts.showCursor:
            with glroutines.enabled((gl.GL_DEPTH_TEST)):
                self.__drawCursor()

        if opts.showLegend:
            self.__drawLegend()

        if opts.showLight:
            self.__drawLight()

        # Testing click-to-near/far clipping plane transformation
        if hasattr(self, 'points'):
            colours = [(1, 0, 0, 1), (0, 0, 1, 1)]
            gl.glPointSize(5)

            gl.glBegin(gl.GL_LINES)
            for i, p in enumerate(self.points):
                gl.glColor4f(*colours[i % 2])
                p = affine.transform(p, self.viewMatrix)
                gl.glVertex3f(*p)
            gl.glEnd()