def drawAll(self, axes, zposes, xforms): """Calls the version-dependent ``drawAll`` function. """ opts = self.opts outline = opts.outline owidth = float(opts.outlineWidth) rtex = self.renderTexture w, h = self.canvas.GetSize() lo, hi = self.canvas.getViewport() xax = axes[0] yax = axes[1] xmin, xmax = lo[xax], hi[xax] ymin, ymax = lo[yax], hi[yax] offsets = [owidth / w, owidth / h] # draw all slices to the offscreen texture with glroutines.disabled(gl.GL_BLEND), rtex.target(xax, yax, lo, hi): fslgl.gllabel_funcs.drawAll(self, axes, zposes, xforms) # run it through the edge filter self.edgeFilter.set(offsets=offsets, outline=outline) self.edgeFilter.apply(rtex, max(zposes), xmin, xmax, ymin, ymax, xax, yax, textureUnit=gl.GL_TEXTURE2)
def draw2D(self, zpos, axes, xform=None, bbox=None): """Calls the version-dependent ``draw2D`` function, then applies the edge filter if necessary. """ opts = self.opts if not opts.outline: fslgl.glmask_funcs.draw2D(self, zpos, axes, xform, bbox) return owidth = float(opts.outlineWidth) rtex = self.renderTexture w, h = self.canvas.GetSize() lo, hi = self.canvas.getViewport() xax = axes[0] yax = axes[1] xmin, xmax = lo[xax], hi[xax] ymin, ymax = lo[yax], hi[yax] offsets = [owidth / w, owidth / h] # Draw the mask to the off-screen texture with glroutines.disabled(gl.GL_BLEND), rtex.target(xax, yax, lo, hi): fslgl.glmask_funcs.draw2D(self, zpos, axes, xform, bbox) # Run the texture through an edge detection # filter, drawing the result to screen self.edgeFilter.set(offsets=offsets, outline=1) self.edgeFilter.apply( rtex, zpos, xmin, xmax, ymin, ymax, xax, yax, textureUnit=gl.GL_TEXTURE1)
def __refreshTexture(self, tex, idx): """Refreshes the given :class:`.RenderTexture`. :arg tex: The ``RenderTexture`` to refresh. :arg idx: Index of the ``RenderTexture``. """ globj = self.__globj zpos = self.__indexToZpos(idx) xax = self.__xax yax = self.__yax axes = (self.__xax, self.__yax, self.__zax) if not globj.ready(): return lo, hi = globj.getDisplayBounds() res = globj.getDataResolution(xax, yax) if res is not None: width = res[xax] height = res[yax] else: width = self.__defaultWidth height = self.__defaultHeight if width > self.__maxWidth: width = self.__maxWidth if height > self.__maxHeight: height = self.__maxHeight log.debug('Refreshing render texture for slice {} (zpos {}, ' 'zax {}): {} x {}'.format(idx, zpos, self.__zax, width, height)) tex.setSize(width, height) oldSize = gl.glGetIntegerv(gl.GL_VIEWPORT) oldProjMat = gl.glGetFloatv( gl.GL_PROJECTION_MATRIX) oldMVMat = gl.glGetFloatv( gl.GL_MODELVIEW_MATRIX) tex.bindAsRenderTarget() glroutines.show2D(xax, yax, width, height, lo, hi) glroutines.clear((0, 0, 0, 0)) with glroutines.disabled(gl.GL_BLEND): globj.preDraw() globj.draw2D(zpos, axes) globj.postDraw() tex.unbindAsRenderTarget() gl.glViewport(*oldSize) gl.glMatrixMode(gl.GL_PROJECTION) gl.glLoadMatrixf(oldProjMat) gl.glMatrixMode(gl.GL_MODELVIEW) gl.glLoadMatrixf(oldMVMat) self.__textureDirty[idx] = False
def drawAll(self, axes, zposes, xforms): """Calls the version-dependent ``drawAll`` function, then applies the edge filter if necessary. """ opts = self.opts rtex = self.renderTexture # Is taking max(z) hacky? It seems to work ok. zpos = max(zposes) owidth = opts.outlineWidth w, h = self.canvas.GetSize() lo, hi = self.canvas.getViewport() xax = axes[0] yax = axes[1] xmin, xmax = lo[xax], hi[xax] ymin, ymax = lo[yax], hi[yax] offsets = [owidth / w, owidth / h] # Draw all slices to the off-screen texture with glroutines.disabled(gl.GL_BLEND), rtex.bound(xax, yax, lo, hi): fslgl.glmask_funcs.drawAll(self, axes, zposes, xforms) # if no outline, draw the texture directly if not opts.outline: self.renderTexture.drawOnBounds(zpos, xmin, xmax, ymin, ymax, xax, yax, textureUnit=gl.GL_TEXTURE1) else: # Run the texture through an edge detection # filter, drawing the result to screen self.edgeFilter.set(offsets=offsets, outline=1) self.edgeFilter.apply(self.renderTexture, zpos, xmin, xmax, ymin, ymax, xax, yax, textureUnit=gl.GL_TEXTURE1)
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 drawCrossSection(self, zpos, axes, lo, hi, dest): """Renders a filled cross-section of the mesh to an off-screen :class:`.RenderTexture`. See: http://glbook.gamedev.net/GLBOOK/glbook.gamedev.net/moglgp/advclip.html :arg zpos: Position along the z axis :arg axes: Tuple containing ``(x, y, z)`` axis indices. :arg lo: Tuple containing the low bounds on each axis. :arg hi: Tuple containing the high bounds on each axis. :arg dest: The :class:`.RenderTexture` to render to. """ opts = self.opts xax = axes[0] yax = axes[1] zax = axes[2] xmin = lo[xax] ymin = lo[yax] xmax = hi[xax] ymax = hi[yax] vertices = self.vertices.ravel('C') indices = self.indices dest.bindAsRenderTarget() dest.setRenderViewport(xax, yax, lo, hi) # Figure out the equation of a plane # perpendicular to the Z axis, and # located at the z position. This is # used as a clipping plane to draw # the mesh intersection. clipPlaneVerts = np.zeros((4, 3), dtype=np.float32) clipPlaneVerts[0, [xax, yax]] = [xmin, ymin] clipPlaneVerts[1, [xax, yax]] = [xmin, ymax] clipPlaneVerts[2, [xax, yax]] = [xmax, ymax] clipPlaneVerts[3, [xax, yax]] = [xmax, ymin] clipPlaneVerts[:, zax] = zpos planeEq = glroutines.planeEquation(clipPlaneVerts[0, :], clipPlaneVerts[1, :], clipPlaneVerts[2, :]) gl.glClearColor(0, 0, 0, 0) gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT | gl.GL_STENCIL_BUFFER_BIT) gl.glEnableClientState(gl.GL_VERTEX_ARRAY) gl.glEnable(gl.GL_CLIP_PLANE0) gl.glEnable(gl.GL_CULL_FACE) gl.glEnable(gl.GL_STENCIL_TEST) gl.glFrontFace(gl.GL_CCW) gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL) gl.glClipPlane(gl.GL_CLIP_PLANE0, planeEq) gl.glColorMask(gl.GL_FALSE, gl.GL_FALSE, gl.GL_FALSE, gl.GL_FALSE) # First and second passes - render front and # back faces separately. In the stencil buffer, # subtract the mask created by the second # render from the mask created by the first - # this gives us a mask which shows the # intersection of the mesh with the clipping # plane. gl.glStencilFunc(gl.GL_ALWAYS, 0, 0) direction = [gl.GL_INCR, gl.GL_DECR] # If the mesh coordinate transformation # has a negative determinant, it means # the back faces will be facing the camera, # so we need to render the back faces first. if npla.det(opts.getCoordSpaceTransform()) > 0: faceOrder = [gl.GL_FRONT, gl.GL_BACK] else: faceOrder = [gl.GL_BACK, gl.GL_FRONT] for face, direction in zip(faceOrder, direction): gl.glStencilOp(gl.GL_KEEP, gl.GL_KEEP, direction) gl.glCullFace(face) gl.glVertexPointer(3, gl.GL_FLOAT, 0, vertices) gl.glDrawElements(gl.GL_TRIANGLES, len(indices), gl.GL_UNSIGNED_INT, indices) # Third pass - render the intersection # of the front and back faces from the # stencil buffer to the render texture. gl.glColorMask(gl.GL_TRUE, gl.GL_TRUE, gl.GL_TRUE, gl.GL_TRUE) gl.glDisable(gl.GL_CLIP_PLANE0) gl.glDisable(gl.GL_CULL_FACE) gl.glDisableClientState(gl.GL_VERTEX_ARRAY) gl.glStencilFunc(gl.GL_NOTEQUAL, 0, 255) gl.glColor(*opts.getConstantColour()) # Disable alpha blending - we # just want the colour copied # into the texture as-is. with glroutines.disabled(gl.GL_BLEND): gl.glBegin(gl.GL_QUADS) gl.glVertex3f(*clipPlaneVerts[0, :]) gl.glVertex3f(*clipPlaneVerts[1, :]) gl.glVertex3f(*clipPlaneVerts[2, :]) gl.glVertex3f(*clipPlaneVerts[3, :]) gl.glEnd() gl.glDisable(gl.GL_STENCIL_TEST) dest.unbindAsRenderTarget() dest.restoreViewport()