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)
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)
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)
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()
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)
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)
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'))
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()
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()
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)
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)
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()
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)
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
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
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()