Skip to content
Snippets Groups Projects
Commit 62310d9f authored by Paul McCarthy's avatar Paul McCarthy
Browse files

Default setAxes method is now implemented in the GLObject class, and

default values given to xax/yax/zax in
GLObject.__init__. generateVertices method, used by image objects which
display a slice through image texture, is implemenmted in GLImageObject
class, purely to eliminate code duplication. gllabel_funcs module now
has an init function. Other cosmetic changes.
parent 9eaa7dfd
No related branches found
No related tags found
No related merge requests found
...@@ -12,10 +12,10 @@ import OpenGL.raw.GL._types as gltypes ...@@ -12,10 +12,10 @@ import OpenGL.raw.GL._types as gltypes
import fsl.fslview.gl.shaders as shaders import fsl.fslview.gl.shaders as shaders
import glvolume_funcs import glvolume_funcs
# TODO make an init() function
def compileShaders(self): def compileShaders(self):
self.vertexAttrBuffer = gl.glGenBuffers(1) if self.shaders is not None:
gl.glDeleteProgram(self.shaders)
vertShaderSrc = shaders.getVertexShader( self, vertShaderSrc = shaders.getVertexShader( self,
sw=self.display.softwareMode) sw=self.display.softwareMode)
...@@ -46,6 +46,16 @@ def compileShaders(self): ...@@ -46,6 +46,16 @@ def compileShaders(self):
self.outlineOffsetsPos = gl.glGetUniformLocation(self.shaders, self.outlineOffsetsPos = gl.glGetUniformLocation(self.shaders,
'outlineOffsets') 'outlineOffsets')
def init(self):
self.shaders = None
compileShaders( self)
updateShaderState(self)
self.vertexAttrBuffer = gl.glGenBuffers(1)
def destroy(self): def destroy(self):
gl.glDeleteBuffers(1, gltypes.GLuint(self.vertexAttrBuffer)) gl.glDeleteBuffers(1, gltypes.GLuint(self.vertexAttrBuffer))
gl.glDeleteProgram(self.shaders) gl.glDeleteProgram(self.shaders)
...@@ -53,8 +63,7 @@ def destroy(self): ...@@ -53,8 +63,7 @@ def destroy(self):
def updateShaderState(self): def updateShaderState(self):
display = self.display opts = self.displayOpts
opts = self.displayOpts
gl.glUseProgram(self.shaders) gl.glUseProgram(self.shaders)
......
...@@ -108,8 +108,7 @@ def updateShaderState(self): ...@@ -108,8 +108,7 @@ def updateShaderState(self):
current display properties. current display properties.
""" """
display = self.display opts = self.displayOpts
opts = self.displayOpts
gl.glUseProgram(self.shaders) gl.glUseProgram(self.shaders)
...@@ -124,10 +123,10 @@ def updateShaderState(self): ...@@ -124,10 +123,10 @@ def updateShaderState(self):
# range, but the shader needs them to be in image # range, but the shader needs them to be in image
# texture value range (0.0 - 1.0). So let's scale # texture value range (0.0 - 1.0). So let's scale
# them. # them.
clipLow = opts.clippingRange[0] * \ clipLow = opts.clippingRange[0] * \
self.imageTexture.invVoxValXform[0, 0] + \ self.imageTexture.invVoxValXform[0, 0] + \
self.imageTexture.invVoxValXform[3, 0] self.imageTexture.invVoxValXform[3, 0]
clipHigh = opts.clippingRange[1] * \ clipHigh = opts.clippingRange[1] * \
self.imageTexture.invVoxValXform[0, 0] + \ self.imageTexture.invVoxValXform[0, 0] + \
self.imageTexture.invVoxValXform[3, 0] self.imageTexture.invVoxValXform[3, 0]
......
#!/usr/bin/env python #!/usr/bin/env python
# #
# gllabel.py - # gllabel.py - OpenGL representation for label/atlas images.
# #
# Author: Paul McCarthy <pauldmccarthy@gmail.com> # Author: Paul McCarthy <pauldmccarthy@gmail.com>
# #
import OpenGL.GL as gl import OpenGL.GL as gl
import fsl.utils.transform as transform import fsl.fslview.gl as fslgl
import fsl.fslview.gl as fslgl import resources as glresources
import resources as glresources import globject
import routines as glroutines import textures
import globject
import textures
class GLLabel(globject.GLImageObject): class GLLabel(globject.GLImageObject):
...@@ -32,14 +30,21 @@ class GLLabel(globject.GLImageObject): ...@@ -32,14 +30,21 @@ class GLLabel(globject.GLImageObject):
imageTexName, imageTexName,
image, image,
display) display)
fslgl.gllabel_funcs.compileShaders(self) fslgl.gllabel_funcs.init(self)
self.refreshLutTexture() self.refreshLutTexture()
self.addListeners() self.addListeners()
def destroy(self):
glresources.delete(self.imageTexture.getTextureName())
self.lutTexture.destroy()
self.removeListeners()
fslgl.gllabel_funcs.destroy(self)
def addListeners(self): def addListeners(self):
...@@ -68,6 +73,10 @@ class GLLabel(globject.GLImageObject): ...@@ -68,6 +73,10 @@ class GLLabel(globject.GLImageObject):
self.__lut = opts.lut self.__lut = opts.lut
# TODO If you add a software shader, you will
# need to call gllabel_funcs.compileShaders
# when display.softwareMode changes
opts .addListener('outline', self.name, shaderUpdate) opts .addListener('outline', self.name, shaderUpdate)
opts .addListener('outlineWidth', self.name, shaderUpdate) opts .addListener('outlineWidth', self.name, shaderUpdate)
opts .addListener('lut', self.name, lutChanged) opts .addListener('lut', self.name, lutChanged)
...@@ -75,7 +84,6 @@ class GLLabel(globject.GLImageObject): ...@@ -75,7 +84,6 @@ class GLLabel(globject.GLImageObject):
display .addListener('alpha', self.name, lutUpdate) display .addListener('alpha', self.name, lutUpdate)
display .addListener('brightness', self.name, lutUpdate) display .addListener('brightness', self.name, lutUpdate)
display .addListener('contrast', self.name, lutUpdate) display .addListener('contrast', self.name, lutUpdate)
def removeListeners(self): def removeListeners(self):
...@@ -89,40 +97,11 @@ class GLLabel(globject.GLImageObject): ...@@ -89,40 +97,11 @@ class GLLabel(globject.GLImageObject):
display.removeListener('brightness', self.name) display.removeListener('brightness', self.name)
display.removeListener('contrast', self.name) display.removeListener('contrast', self.name)
def destroy(self):
glresources.delete(self.imageTexture.getTextureName())
self.lutTexture.destroy()
self.removeListeners()
fslgl.gllabel_funcs.destroy(self)
def setAxes(self, xax, yax): def setAxes(self, xax, yax):
"""This method should be called when the image display axes change.""" globject.GLImageObject.setAxes(self, xax, yax)
self.xax = xax
self.yax = yax
self.zax = 3 - xax - yax
fslgl.gllabel_funcs.updateShaderState(self) fslgl.gllabel_funcs.updateShaderState(self)
def generateVertices(self, zpos, xform):
vertices, voxCoords, texCoords = glroutines.slice2D(
self.image.shape[:3],
self.xax,
self.yax,
zpos,
self.displayOpts.getTransform('voxel', 'display'),
self.displayOpts.getTransform('display', 'voxel'))
if xform is not None:
vertices = transform.transform(vertices, xform)
return vertices, voxCoords, texCoords
def refreshLutTexture(self, *a): def refreshLutTexture(self, *a):
......
...@@ -104,10 +104,7 @@ class GLModel(globject.GLObject): ...@@ -104,10 +104,7 @@ class GLModel(globject.GLObject):
def setAxes(self, xax, yax): def setAxes(self, xax, yax):
self.xax = xax globject.GLObject.setAxes(self, xax, yax)
self.yax = yax
self.zax = 3 - xax - yax
self._renderTexture.setAxes(xax, yax) self._renderTexture.setAxes(xax, yax)
......
...@@ -15,6 +15,9 @@ representation. ...@@ -15,6 +15,9 @@ representation.
import numpy as np import numpy as np
import routines as glroutines
import fsl.utils.transform as transform
def createGLObject(overlay, display): def createGLObject(overlay, display):
"""Create :class:`GLObject` instance for the given overlay, as specified """Create :class:`GLObject` instance for the given overlay, as specified
...@@ -34,21 +37,43 @@ def createGLObject(overlay, display): ...@@ -34,21 +37,43 @@ def createGLObject(overlay, display):
class GLObject(object): class GLObject(object):
"""The :class:`GLObject` class is a superclass for all 2D OpenGL """The :class:`GLObject` class is a superclass for all 2D OpenGL
objects. objects.
The following attributes will always be available on ``GLObject``
instances:
- ``name``: A unique name for this ``GLObject`` instance.
- ``xax``: Index of the display coordinate system axis that
corresponds to the horizontal screen axis.
- ``yax``: Index of the display coordinate system axis that
corresponds to the vertical screen axis.
- ``zax``: Index of the display coordinate system axis that
corresponds to the depth screen axis.
""" """
def __init__(self): def __init__(self):
"""Create a :class:`GLObject`. The constructor adds one attribute to """Create a :class:`GLObject`. The constructor adds one attribute
this instance, ``name``, which is simply a unique name for this to this instance, ``name``, which is simply a unique name for this
instance. instance, and gives default values to the ``xax``, ``yax``, and
``zax`` attributes.
Subclass implementations must call this method, and should also Subclass implementations must call this method, and should also
perform any necessary OpenGL initialisation, such as creating perform any necessary OpenGL initialisation, such as creating
textures. textures.
""" """
# Give this instance a name, and set
# initial values for the display axes
self.name = '{}_{}'.format(type(self).__name__, id(self)) self.name = '{}_{}'.format(type(self).__name__, id(self))
self.xax = 0
self.yax = 1
self.zax = 2
self.__updateListeners = {} self.__updateListeners = {}
def addUpdateListener(self, name, listener): def addUpdateListener(self, name, listener):
"""Adds a listener function which will be called whenever this """Adds a listener function which will be called whenever this
...@@ -87,10 +112,15 @@ class GLObject(object): ...@@ -87,10 +112,15 @@ class GLObject(object):
def setAxes(self, xax, yax): def setAxes(self, xax, yax):
"""This method is called when the display orientation for this """This method is called when the display orientation for this
:class:`GLObject` changes. It should perform any necessary updates to :class:`GLObject` changes. It sets :attr:`xax`, :attr:`yax`,
the GL data (e.g. regenerating/moving vertices). and :attr:`zax` attributes on this ``GLObject`` instance.
Subclass implementations should call this method, or should set
the ``xax``, ``yax``, and ``zax`` attributes themselves.
""" """
raise NotImplementedError() self.xax = xax
self.yax = yax
self.zax = 3 - xax - yax
def destroy(self): def destroy(self):
...@@ -131,7 +161,7 @@ class GLObject(object): ...@@ -131,7 +161,7 @@ class GLObject(object):
``xforms`` arrays. ``xforms`` arrays.
In some circumstances (hint: the :class:`.LightBoxCanvas`), better In some circumstances (hint: the :class:`.LightBoxCanvas`), better
performance may be achievbed in combining multiple renders, rather performance may be achieved in combining multiple renders, rather
than doing it with separate calls to :meth:`draw`. than doing it with separate calls to :meth:`draw`.
The default implementation does exactly this, so this method need only The default implementation does exactly this, so this method need only
...@@ -160,27 +190,12 @@ class GLSimpleObject(GLObject): ...@@ -160,27 +190,12 @@ class GLSimpleObject(GLObject):
Subclasses should not assume that any of the other methods will ever Subclasses should not assume that any of the other methods will ever
be called. be called.
On calls to :meth:`draw`, the following attributes will be available on
``GLSimpleObject`` instances:
- ``xax``: Index of the display coordinate system axis that corresponds
to the horizontal screen axis.
- ``yax``: Index of the display coordinate system axis that corresponds
to the vertical screen axis.
""" """
def __init__(self): def __init__(self):
GLObject.__init__(self) GLObject.__init__(self)
def setAxes(self, xax, yax): def destroy( self): pass
self.xax = xax
self.yax = yax
self.zax = 3 - xax - yax
def destroy(self): pass
def preDraw( self): pass def preDraw( self): pass
def postDraw(self): pass def postDraw(self): pass
...@@ -234,6 +249,37 @@ class GLImageObject(GLObject): ...@@ -234,6 +249,37 @@ class GLImageObject(GLObject):
minres = int(round(((hi - lo) / res).min())) minres = int(round(((hi - lo) / res).min()))
return [minres] * 3 return [minres] * 3
def generateVertices(self, zpos, xform):
"""Generates vertex coordinates for a 2D slice through the given
``zpos``, with the optional ``xform`` applied to the coordinates.
This method is called by the :mod:`.gl14.glvolume_funcs` and
:mod:`.gl21.glvolume_funcs` modules.
A tuple of three values is returned, containing:
- A ``6*3 numpy.float32`` array containing the vertex coordinates
- A ``6*3 numpy.float32`` array containing the voxel coordinates
corresponding to each vertex
- A ``6*3 numpy.float32`` array containing the texture coordinates
corresponding to each vertex
"""
vertices, voxCoords, texCoords = glroutines.slice2D(
self.image.shape[:3],
self.xax,
self.yax,
zpos,
self.displayOpts.getTransform('voxel', 'display'),
self.displayOpts.getTransform('display', 'voxel'))
if xform is not None:
vertices = transform.transform(vertices, xform)
return vertices, voxCoords, texCoords
import glvolume import glvolume
import glmask import glmask
......
...@@ -10,9 +10,7 @@ vector images in RGB mode. ...@@ -10,9 +10,7 @@ vector images in RGB mode.
import numpy as np import numpy as np
import fsl.fslview.gl as fslgl import fsl.fslview.gl as fslgl
import fsl.fslview.gl.routines as glroutines
import fsl.fslview.gl.glvector as glvector import fsl.fslview.gl.glvector as glvector
import fsl.utils.transform as transform
class GLRGBVector(glvector.GLVector): class GLRGBVector(glvector.GLVector):
...@@ -28,21 +26,6 @@ class GLRGBVector(glvector.GLVector): ...@@ -28,21 +26,6 @@ class GLRGBVector(glvector.GLVector):
fslgl.glrgbvector_funcs.init(self) fslgl.glrgbvector_funcs.init(self)
def generateVertices(self, zpos, xform):
vertices, voxCoords, texCoords = glroutines.slice2D(
self.image.shape[:3],
self.xax,
self.yax,
zpos,
self.displayOpts.getTransform('voxel', 'display'),
self.displayOpts.getTransform('display', 'voxel'))
if xform is not None:
vertices = transform.transform(vertices, xform)
return vertices, voxCoords, texCoords
def compileShaders(self): def compileShaders(self):
fslgl.glrgbvector_funcs.compileShaders(self) fslgl.glrgbvector_funcs.compileShaders(self)
......
...@@ -276,14 +276,6 @@ class GLVector(globject.GLImageObject): ...@@ -276,14 +276,6 @@ class GLVector(globject.GLImageObject):
texture.set(cmap=cmap, displayRange=drange) texture.set(cmap=cmap, displayRange=drange)
def setAxes(self, xax, yax):
"""Stores the new x/y/z axes."""
self.xax = xax
self.yax = yax
self.zax = 3 - xax - yax
def preDraw(self): def preDraw(self):
"""Must be called by subclass implementations. """Must be called by subclass implementations.
......
...@@ -53,14 +53,12 @@ of the image bounds. ...@@ -53,14 +53,12 @@ of the image bounds.
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
import OpenGL.GL as gl import OpenGL.GL as gl
import fsl.utils.transform as transform import fsl.fslview.gl as fslgl
import fsl.fslview.gl as fslgl import textures
import textures import globject
import globject import resources as glresources
import routines as glroutines
import resources as glresources
class GLVolume(globject.GLImageObject): class GLVolume(globject.GLImageObject):
...@@ -106,14 +104,6 @@ class GLVolume(globject.GLImageObject): ...@@ -106,14 +104,6 @@ class GLVolume(globject.GLImageObject):
fslgl.glvolume_funcs.init(self) fslgl.glvolume_funcs.init(self)
def setAxes(self, xax, yax):
"""This method should be called when the image display axes change."""
self.xax = xax
self.yax = yax
self.zax = 3 - xax - yax
def preDraw(self): def preDraw(self):
"""Sets up the GL state to draw a slice from this :class:`GLVolume` """Sets up the GL state to draw a slice from this :class:`GLVolume`
instance. instance.
...@@ -125,37 +115,6 @@ class GLVolume(globject.GLImageObject): ...@@ -125,37 +115,6 @@ class GLVolume(globject.GLImageObject):
fslgl.glvolume_funcs.preDraw(self) fslgl.glvolume_funcs.preDraw(self)
def generateVertices(self, zpos, xform=None):
"""Generates vertex coordinates for a 2D slice through the given
``zpos``, with the optional ``xform`` applied to the coordinates.
This method is called by the :mod:`.gl14.glvolume_funcs` and
:mod:`.gl21.glvolume_funcs` modules.
A tuple of three values is returned, containing:
- A ``6*3 numpy.float32`` array containing the vertex coordinates
- A ``6*3 numpy.float32`` array containing the voxel coordinates
corresponding to each vertex
- A ``6*3 numpy.float32`` array containing the texture coordinates
corresponding to each vertex
"""
vertices, voxCoords, texCoords = glroutines.slice2D(
self.image.shape[:3],
self.xax,
self.yax,
zpos,
self.displayOpts.getTransform('voxel', 'display'),
self.displayOpts.getTransform('display', 'voxel'))
if xform is not None:
vertices = transform.transform(vertices, xform)
return vertices, voxCoords, texCoords
def draw(self, zpos, xform=None): def draw(self, zpos, xform=None):
"""Draws a 2D slice of the image at the given real world Z location. """Draws a 2D slice of the image at the given real world Z location.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment