diff --git a/fsl/fslview/gl/gl14/glvolume_funcs.py b/fsl/fslview/gl/gl14/glvolume_funcs.py index 875b1d7b9fa7cbb74d5a297b7e9b4a7d30c9400a..9c43fdc3e63b5934cc7c9d1e257091f6edbf8332 100644 --- a/fsl/fslview/gl/gl14/glvolume_funcs.py +++ b/fsl/fslview/gl/gl14/glvolume_funcs.py @@ -30,9 +30,9 @@ This PDF is quite useful: """ import logging -log = logging.getLogger(__name__) import OpenGL.GL as gl +import OpenGL.raw.GL._types as gltypes import OpenGL.GL.ARB.fragment_program as arbfp import OpenGL.GL.ARB.vertex_program as arbvp @@ -40,6 +40,9 @@ import fsl.utils.transform as transform import fsl.fslview.gl.shaders as shaders +log = logging.getLogger(__name__) + + def init(glvol, xax, yax): """Compiles the vertex and fragment programs used for rendering.""" @@ -55,8 +58,9 @@ def init(glvol, xax, yax): def destroy(glvol): """Deletes handles to the vertex/fragment programs.""" - arbvp.glDeleteProgramsARB(glvol.vertexProgram) - arbfp.glDeleteProgramsARB(glvol.fragmentProgram) + + arbvp.glDeleteProgramsARB(1, gltypes.GLuint(glvol.vertexProgram)) + arbfp.glDeleteProgramsARB(1, gltypes.GLuint(glvol.fragmentProgram)) def genVertexData(glvol): @@ -213,10 +217,12 @@ def postDraw(glvol): gl.glMatrixMode(gl.GL_TEXTURE) gl.glActiveTexture(gl.GL_TEXTURE0) + gl.glBindTexture(gl.GL_TEXTURE_3D, 0) gl.glPopMatrix() gl.glMatrixMode(gl.GL_TEXTURE) gl.glActiveTexture(gl.GL_TEXTURE1) + gl.glBindTexture(gl.GL_TEXTURE_1D, 0) gl.glPopMatrix() gl.glDisable(gl.GL_TEXTURE_1D) diff --git a/fsl/fslview/gl/gl21/glvolume_funcs.py b/fsl/fslview/gl/gl21/glvolume_funcs.py index 60e95a28a5e5ac5265ee61750ef7e4c5fc7a2237..a2aae8b28b48c8d5831f5cda5de3013af9439845 100644 --- a/fsl/fslview/gl/gl21/glvolume_funcs.py +++ b/fsl/fslview/gl/gl21/glvolume_funcs.py @@ -26,9 +26,9 @@ This module provides the following functions: import logging log = logging.getLogger(__name__) -import numpy as np -import OpenGL.GL as gl -import OpenGL.arrays.vbo as vbo +import numpy as np +import OpenGL.GL as gl +import OpenGL.raw.GL._types as gltypes import fsl.fslview.gl.shaders as shaders import fsl.utils.transform as transform @@ -66,10 +66,10 @@ def _compileShaders(glvol): 'colourTexture') glvol.useSplinePos = gl.glGetUniformLocation(glvol.shaders, 'useSpline') - glvol.displayToVoxMatPos = gl.glGetUniformLocation(glvol.shaders, - 'displayToVoxMat') - glvol.voxValXformPos = gl.glGetUniformLocation(glvol.shaders, - 'voxValXform') + glvol.displayToVoxMatPos = gl.glGetUniformLocation(glvol.shaders, + 'displayToVoxMat') + glvol.voxValXformPos = gl.glGetUniformLocation(glvol.shaders, + 'voxValXform') def init(glvol, xax, yax): @@ -77,11 +77,16 @@ def init(glvol, xax, yax): """ _compileShaders(glvol) + glvol.worldCoordBuffer = gl.glGenBuffers(1) + glvol.indexBuffer = gl.glGenBuffers(1) + def destroy(glvol): """Cleans up VBO handles.""" - glvol.worldCoords.delete() - glvol.indices .delete() + + gl.glDeleteBuffers(1, gltypes.GLuint(glvol.worldCoordBuffer)) + gl.glDeleteBuffers(1, gltypes.GLuint(glvol.indexBuffer)) + gl.glDeleteProgram(glvol.shaders) def genVertexData(glvol): @@ -92,13 +97,26 @@ def genVertexData(glvol): xax = glvol.xax yax = glvol.yax + worldCoordBuffer = glvol.worldCoordBuffer + indexBuffer = glvol.indexBuffer worldCoords, indices = glvol.genVertexData() worldCoords = worldCoords[:, [xax, yax]] - worldCoordBuffer = vbo.VBO(worldCoords.ravel('C'), gl.GL_STATIC_DRAW) - indexBuffer = vbo.VBO(indices .ravel('C'), gl.GL_STATIC_DRAW, - gl.GL_ELEMENT_ARRAY_BUFFER) + worldCoords = worldCoords.ravel('C') + indices = indices .ravel('C') + + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, worldCoordBuffer) + gl.glBufferData(gl.GL_ARRAY_BUFFER, + worldCoords.nbytes, + worldCoords, + gl.GL_STATIC_DRAW) + + gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, indexBuffer) + gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, + indices.nbytes, + indices, + gl.GL_STATIC_DRAW) return worldCoordBuffer, indexBuffer, len(indices) @@ -155,7 +173,7 @@ def preDraw(glvol): gl.glUniform1i(glvol.imageTexturePos, 1) # Bind the world x/y coordinate buffer - glvol.worldCoords.bind() + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, glvol.worldCoords) gl.glVertexAttribPointer( glvol.worldCoordPos, 2, @@ -166,7 +184,7 @@ def preDraw(glvol): gl.glEnableVertexAttribArray(glvol.worldCoordPos) # Bind the vertex index buffer - glvol.indices.bind() + gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, glvol.indices) def draw(glvol, zpos, xform=None): @@ -214,7 +232,7 @@ def postDraw(glvol): gl.glDisable(gl.GL_TEXTURE_1D) gl.glDisable(gl.GL_TEXTURE_3D) - glvol.indices .unbind() - glvol.worldCoords.unbind() + gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0) + gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, 0) gl.glUseProgram(0) diff --git a/fsl/fslview/gl/glvolume.py b/fsl/fslview/gl/glvolume.py index e9ce5c1bdf4577e90da545bfb891c45f0979cb30..5822d4a2a53ec8aaf2fb48eaf79c51757f64afcf 100644 --- a/fsl/fslview/gl/glvolume.py +++ b/fsl/fslview/gl/glvolume.py @@ -51,6 +51,7 @@ import numpy as np import fsl.fslview.gl as fslgl import fsl.fslview.gl.globject as globject +import fsl.utils.transform as transform class GLVolume(globject.GLObject): @@ -85,13 +86,13 @@ class GLVolume(globject.GLObject): # Only one 'GLVolumeDirty' listener, for all GLVolume # instances, is registered on ecah image/display, # so the GLVolumeDirty attribute is only set once. - try: display.addListener('interpolation', name, markImage) + try: display.addListener('interpolation', name, markImage) except: pass - try: display.addListener('volume', name, markImage) + try: display.addListener('volume', name, markImage) except: pass - try: display.addListener('resolution', name, markImage) + try: display.addListener('resolution', name, markImage) except: pass - try: image .addListener('data', name, markImage) + try: image .addListener('data', name, markImage) except: pass @@ -183,7 +184,7 @@ class GLVolume(globject.GLObject): deleting texture handles). """ log.debug('Deleting GL texture: {}'.format(self.colourTexture)) - gl.glDeleteTextures(1, self.colourTexture) + gl.glDeleteTextures(self.colourTexture) # Another GLVolume object may have # already deleted the image texture @@ -191,13 +192,12 @@ class GLVolume(globject.GLObject): imageTexture = self.image.delAttribute( '{}Texture'.format(type(self).__name__)) log.debug('Deleting GL texture: {}'.format(imageTexture)) - gl.glDeleteTextures(1, imageTexture) + gl.glDeleteTextures(imageTexture) except KeyError: pass self.removeDisplayListeners() - fslgl.glvolume_funcs.destroy(self) @@ -303,9 +303,7 @@ class GLVolume(globject.GLObject): elif dtype == np.int16: scale = 65535 else: scale = dmax - dmin - voxValXform = np.eye(4, dtype=np.float32) - voxValXform[0, 0] = scale - voxValXform[3, 0] = offset + voxValXform = transform.scaleOffsetXform(scale, offset) return data, texIntFmt, texExtFmt, voxValXform diff --git a/fsl/fslview/gl/shaders.py b/fsl/fslview/gl/shaders.py index 3c5490c6756782057cd81f2bb4eeb77914442cd0..7a75513fec9caa28fa66669293e5fa2e03aec6d4 100644 --- a/fsl/fslview/gl/shaders.py +++ b/fsl/fslview/gl/shaders.py @@ -24,9 +24,7 @@ import logging import os.path as op -import fsl.fslview.gl as fslgl -import fsl.fslview.gl.glvolume as glvolume -import fsl.fslview.gl.gltensor as gltensor +import fsl.fslview.gl as fslgl log = logging.getLogger(__name__) @@ -173,6 +171,9 @@ def _getFileName(globj, shaderType): # callers can request a specific # shader by passing the name, rather # than passing a GLObject instance + import fsl.fslview.gl.glvolume as glvolume + import fsl.fslview.gl.gltensor as gltensor + if isinstance(globj, str): prefix = globj elif isinstance(globj, glvolume.GLVolume): prefix = 'glvolume' elif isinstance(globj, gltensor.GLTensor): prefix = 'gltensor' diff --git a/fsl/fslview/gl/slicecanvas.py b/fsl/fslview/gl/slicecanvas.py index c60fa54cbcf3a0635c48892d334d439daffee736..6c41004b61a8834c7b7104682ac6823f48bf356c 100644 --- a/fsl/fslview/gl/slicecanvas.py +++ b/fsl/fslview/gl/slicecanvas.py @@ -384,6 +384,19 @@ class SliceCanvas(props.HasProperties): valid=None, name=None, disp=display): + + log.debug('GLObject representation for {} ' + 'changed to {}'.format(display.name, + display.imageType)) + + # Tell the previous GLObject (if + # any) to clean up after itself + try: + globj = image.getAttribute(self.name) + globj.destroy() + except KeyError: + pass + globj = globject.createGLObject(image, disp) opts = display.getDisplayOpts() diff --git a/fsl/utils/transform.py b/fsl/utils/transform.py index 2fc298f726cce1b61042b18ede1595a46e265088..1ffea567dda9e29ae8efd9df46219a4135db71f6 100644 --- a/fsl/utils/transform.py +++ b/fsl/utils/transform.py @@ -23,6 +23,33 @@ def concat(x1, x2): return np.dot(x1, x2) +def scaleOffsetXform(scales, offsets): + """Creates and returns an affine transformation matrix which encodes + the specified scale(s) and offset(s). + """ + + if not isinstance(scales, collections.Sequence): scales = [scales] + if not isinstance(offsets, collections.Sequence): offsets = [offsets] + + lens = len(scales) + leno = len(offsets) + + if lens < 3: scales = scales + [1] * (3 - lens) + if leno < 3: offsets = offsets + [0] * (3 - leno) + + xform = np.eye(4, dtype=np.float32) + + xform[0, 0] = scales[0] + xform[1, 1] = scales[1] + xform[2, 2] = scales[2] + + xform[3, 0] = offsets[0] + xform[3, 1] = offsets[0] + xform[3, 2] = offsets[0] + + return xform + + def axisBounds(shape, xform, axes=None): """Returns the (lo, hi) bounds of the specified axis/axes."""