diff --git a/fsl/fsleyes/displaycontext/tensoropts.py b/fsl/fsleyes/displaycontext/tensoropts.py index 2cbb07b476e3bbf9e6f2484350531193d7c377a0..e3d55e6b9b7e714fdccfa7a3726e848b9629cb65 100644 --- a/fsl/fsleyes/displaycontext/tensoropts.py +++ b/fsl/fsleyes/displaycontext/tensoropts.py @@ -7,15 +7,26 @@ import logging -import volumeopts +import props + +import vectoropts log = logging.getLogger(__name__) -class TensorOpts(volumeopts.ImageOpts): +class TensorOpts(vectoropts.VectorOpts): + + + # Only show 2D ellipses around + # the three primary tensor axes + outline = props.Boolean(default=False) + + # Enable/disable lighting effects + lighting = props.Boolean(default=False) + def __init__(self, *args, **kwargs): - volumeopts.ImageOpts.__init__(self, *args, **kwargs) + vectoropts.VectorOpts.__init__(self, *args, **kwargs) diff --git a/fsl/fsleyes/displaycontext/vectoropts.py b/fsl/fsleyes/displaycontext/vectoropts.py index 079182e5dada1d6ff3cd8a8427892ff32027ed7a..ee3e6bd05213e02317f02eff82c7180304df9c9d 100644 --- a/fsl/fsleyes/displaycontext/vectoropts.py +++ b/fsl/fsleyes/displaycontext/vectoropts.py @@ -19,9 +19,9 @@ import volumeopts class VectorOpts(volumeopts.ImageOpts): - """The ``VectorOpts`` class is the base class for :class:`LineVectorOpts` and - :class:`RGBVectorOpts`. It contains display settings which are common to - both. + """The ``VectorOpts`` class is the base class for :class:`LineVectorOpts`, + :class:`RGBVectorOpts`, and :class:`.TensorOpts`. It contains display + settings which are common to each of them. """ diff --git a/fsl/fsleyes/gl/__init__.py b/fsl/fsleyes/gl/__init__.py index bfd07005d960fbba43e6b82c98f7ca52392e4614..fc892789d59f2216a91476427df7d624135cb3e0 100644 --- a/fsl/fsleyes/gl/__init__.py +++ b/fsl/fsleyes/gl/__init__.py @@ -263,7 +263,10 @@ def bootstrap(glVersion=None): rendering :class:`.GLModel` instances. ``gllabel_funcs`` The version-specific module containing functions for - rendering :class:`.GLLabel` instances. + rendering :class:`.GLLabel` instances. + + ``gltensor_funcs`` The version-specific module containing functions for + rendering :class:`.GLTensor` instances. ====================== ==================================================== @@ -375,6 +378,7 @@ def bootstrap(glVersion=None): thismod.gllinevector_funcs = glpkg.gllinevector_funcs thismod.glmodel_funcs = glpkg.glmodel_funcs thismod.gllabel_funcs = glpkg.gllabel_funcs + thismod.gltensor_funcs = glpkg.gltensor_funcs thismod._bootstrapped = True diff --git a/fsl/fsleyes/gl/gl21/__init__.py b/fsl/fsleyes/gl/gl21/__init__.py index f5873e38593c571d15cbcc7f92cc8ae678074f1b..1b02bcdbdd238d1e3939f96f0fd96996347e7a99 100644 --- a/fsl/fsleyes/gl/gl21/__init__.py +++ b/fsl/fsleyes/gl/gl21/__init__.py @@ -15,6 +15,7 @@ exist: ~fsl.fsleyes.gl.gl21.gllinevector_funcs ~fsl.fsleyes.gl.gl21.glmodel_funcs ~fsl.fsleyes.gl.gl21.gllabel_funcs + ~fsl.fsleyes.gl.gl21.gltensor_funcs """ import glvolume_funcs @@ -22,3 +23,4 @@ import glrgbvector_funcs import gllinevector_funcs import glmodel_funcs import gllabel_funcs +import gltensor_funcs diff --git a/fsl/fsleyes/gl/gl21/gltensor_funcs.py b/fsl/fsleyes/gl/gl21/gltensor_funcs.py new file mode 100644 index 0000000000000000000000000000000000000000..0e925ea1b3e9600f788dcd127c63e6afd1de3de2 --- /dev/null +++ b/fsl/fsleyes/gl/gl21/gltensor_funcs.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# +# gltensor_funcs.py - +# +# Author: Paul McCarthy <pauldmccarthy@gmail.com> +# + +import logging + +import OpenGL.GL as gl + +import fsl.fsleyes.gl as fslgl +import fsl.fsleyes.gl.resources as glresources +import fsl.fsleyes.gl.textures as textures +import fsl.fsleyes.gl.shaders as shaders + + +log = logging.getLogger(__name__) + + +def init(self): + + + image = self.image + + v1 = image.V1() + v2 = image.V2() + v3 = image.V3() + l1 = image.L1() + l2 = image.L2() + l3 = image.L3() + + + def vPrefilter(d): + return d.transpose((3, 0, 1, 2)) + + names = ['v1', 'v2', 'v3', 'l1', 'l2', 'l3'] + imgs = [ v1, v2, v3, l1, l2, l3] + + for name, img in zip(names, imgs): + texName = '{}_{}'.format(type(self).__name__, id(img)) + + if name[0] == 'v': + prefilter = vPrefilter + nvals = 3 + else: + prefilter = None + nvals = 1 + + tex = glresources.get( + texName, + textures.ImageTexture, + texName, + img, + nvals=nvals, + normalise=True, + prefilter=prefilter) + + setattr(self, '{}Texture'.format(name), tex) + + + self.shaders = None + + compileShaders(self) + + +def destroy(self): + pass + + +def compileShaders(self): + if self.shaders is not None: + gl.glDeleteProgram(self.shaders) + + vertShaderSrc = shaders.getVertexShader( self) + fragShaderSrc = shaders.getFragmentShader(self) + + self.shaders = shaders.compileShaders(vertShaderSrc, fragShaderSrc) + + +def updateShaderState(self): + pass + + +def preDraw(self): + pass + + +def draw(self, zpos, xform=None): + pass + + +def postDraw(self): + pass diff --git a/fsl/fsleyes/gl/gl21/gltensor_vert.glsl b/fsl/fsleyes/gl/gl21/gltensor_vert.glsl new file mode 100644 index 0000000000000000000000000000000000000000..6874d1b7eae141c85a05d4ccfec3bfd83a86696d --- /dev/null +++ b/fsl/fsleyes/gl/gl21/gltensor_vert.glsl @@ -0,0 +1,142 @@ +/* + * OpenGL vertex shader used for rendering GLTensor instances. + * + * Author: Paul McCarthy <pauldmccarthy@gmail.com> + */ +#version 120 + +#define PI 3.141592653589793 +#define PI_ON_2 1.5707963267948966 + + +/* + Required inputs: + + - Textures containing V1, V2, V3 and L1, L2, L3 + + - Voxel coordinates (these are the vertices) + - Vertex index + - Ellipsoid resolution + + **We use (index % resolution) to calculate:** + + - U and V angles + + - + + */ + +uniform sampler3D v1Texture; +uniform sampler3D v2Texture; +uniform sampler3D v3Texture; +uniform sampler3D l1Texture; +uniform sampler3D l2Texture; +uniform sampler3D l3Texture; + +uniform mat4 v1ValXform; +uniform mat4 v2ValXform; +uniform mat4 v3ValXform; +uniform mat4 l1ValXform; +uniform mat4 l2ValXform; +uniform mat4 l3ValXform; + +uniform mat4 voxToDisplayMat; + +uniform vec3 imageShape; + +uniform float resolution; + +attribute float index; +attribute vec3 voxel; + +varying vec3 fragVoxCoord; +varying vec3 fragTexCoord; + + +vec3 ellipsoidVertex(float l1, float l2, float l3, float u, float v) { + + float cosu = cos(u); + float cosv = cos(v); + float sinu = sin(u); + float sinv = sin(v); + + float spcu = sign(cosu) * pow(abs(cosu), 2); + float spcv = sign(cosv) * pow(abs(cosv), 2); + float spsu = sign(sinu) * pow(abs(sinu), 2); + float spsv = sign(sinv) * pow(abs(sinv), 2); + + vec3 x; + + x.x = spcu * spcv; + x.y = spcu * spsv; + x.z = spsu; + + return x; +} + + + +void main(void) { + + float umin = -PI_ON_2; + float umax = PI_ON_2; + float vmin = -PI; + float vmax = PI; + float ustep = (umax - umin) / resolution; + float vstep = (vmax - vmin) / resolution; + + // Index of this vertex + // within the ellipsoid + float ellipsoidIndex = mod(index, resolution); + + // Ellipsoid angles for this vertex + float u = umin + ustep * ellipsoidIndex; + float v = vmin + vstep * ellipsoidIndex; + + // Lookup the tensor parameters from the textures + vec3 texCoord = (voxel + 0.5) / imageShape; + + vec3 v1 = texture3D(v1Texture, texCoord).xyz; + vec3 v2 = texture3D(v2Texture, texCoord).xyz; + vec3 v3 = texture3D(v3Texture, texCoord).xyz; + float l1 = texture3D(l1Texture, texCoord).x; + float l2 = texture3D(l2Texture, texCoord).x; + float l3 = texture3D(l3Texture, texCoord).x; + + // Transform from normalised + // texture values to real values + v1 = v1 * v1ValXform[0].x + v1ValXform[3].x; + v2 = v2 * v2ValXform[0].x + v2ValXform[3].x; + v3 = v3 * v3ValXform[0].x + v3ValXform[3].x; + l1 = l1 * l1ValXform[0].x + l1ValXform[3].x; + l2 = l2 * l2ValXform[0].x + l2ValXform[3].x; + l3 = l3 * l3ValXform[0].x + l3ValXform[3].x; + + // Calculate the position of + // this vertex on the ellipsoid. + // Vertices are grouped into quads - + // figure out what corner we're on + vec3 pos; + + float corner = mod(ellipsoidIndex, 4); + + if (corner == 0) pos = ellipsoidVertex(l1, l2, l3, u, v); + else if (corner == 1) pos = ellipsoidVertex(l1, l2, l3, u + ustep, v); + else if (corner == 2) pos = ellipsoidVertex(l1, l2, l3, u + ustep, v + vstep); + else if (corner == 3) pos = ellipsoidVertex(l1, l2, l3, u, v + vstep); + + // Transform the vertex from + // the fibre coordinate system + // to the voxel coordinate system + mat3 eigvecs = mat3(v1, v2, v3); + + pos = pos * eigvecs + voxel; + + // Transform from voxels into display + gl_Position = gl_ModelViewProjectionMatrix * + voxToDisplayMat * + vec4(pos, 1); + + fragVoxCoord = voxel; + fragTexCoord = texCoord; +} diff --git a/fsl/fsleyes/gl/gltensor.py b/fsl/fsleyes/gl/gltensor.py new file mode 100644 index 0000000000000000000000000000000000000000..1be569b2c5daba93bd4e8874cb3869c5211492d9 --- /dev/null +++ b/fsl/fsleyes/gl/gltensor.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# +# gltensor.py - +# +# Author: Paul McCarthy <pauldmccarthy@gmail.com> +# + +import logging + +import glvector + +import fsl.fsleyes.gl as fslgl + +log = logging.getLogger(__name__) + + +class GLTensor(glvector.GLVector): + + + def __init__(self, image, display): + glvector.GLVector.__init__(self, image, display) + + fslgl.gltensor_funcs.init(self) + + + def destroy(self): + glvector.GLVector.destroy(self) + fslgl.gltensor_funcs.destroy(self) + + + def getDataResolution(self, xax, yax): + """Overrides :meth:`.GLVector.getDataResolution`. Returns a pixel + resolution suitable for rendering this ``GLTensor``. + """ + + res = list(glvector.GLVector.getDataResolution(self, xax, yax)) + res[xax] *= 20 + res[yax] *= 20 + + return res + + + def compileShaders(self): + fslgl.gltensor_funcs.compileShaders(self) + + + def updateShaderState(self): + fslgl.gltensor_funcs.updateShaderState(self) + + + def preDraw(self): + glvector.GLVector.preDraw(self) + fslgl.gltensor_funcs.preDraw(self) + + + def draw(self, zpos, xform=None): + fslgl.gltensor_funcs.draw(self, zpos, xform) + + + def postDraw(self): + glvector.GLVector.postDraw(self) + fslgl.gltensor_funcs.postDraw(self) diff --git a/fsl/fsleyes/gl/shaders.py b/fsl/fsleyes/gl/shaders.py index 770c1b937bd225ee761ae3670583cb38dd8c51b0..f1321e6c8074188d64d932d53d08c9fc8b1191e4 100644 --- a/fsl/fsleyes/gl/shaders.py +++ b/fsl/fsleyes/gl/shaders.py @@ -253,6 +253,9 @@ _shaderTypePrefixMap = td.TypeDict({ ('GLModel', 'vert') : 'glmodel', ('GLModel', 'frag') : 'glmodel', + + ('GLTensor', 'vert') : 'gltensor', + ('GLTensor', 'frag') : 'glvector', }) """This dictionary provides a mapping between :class:`.GLObject` types, and file name prefixes, identifying the shader programs to use.