Source code for fsleyes.gl.shaders

#
# __init__.py - Funtions for managing OpenGL shader programs.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""The ``shaders`` package contains classes and functions for finding,
parsing, compiling, and managing OpenGL shader programs. Two types of shader
program are supported:


 - GLSL 1.20 vertex and fragment shaders.

 - ``ARB_vertex_program`` and ``ARB_fragment_program`` shader programs.


The :mod:`.glsl` and :mod:`.arbp` packages respectively define the
:class:`.GLSLShader` and :class:`.ARBPShader` classes, which may be
used to manage shader programs of the corresponding type.


Some package-level functions are defined here, for finding and loading
shader source code:


  .. autosummary::
     :nosignatures:

     getShaderDir
     getShaderSuffix
     getVertexShader
     getFragmentShader
"""


# Import open from the io module, because it gives
# us an interface compatible across python 2 and 3
# (i.e. it allows us to specify the file encoding,
# and thus allows shader files to contain non-ascii
# characters).
from io import                 open
import os.path              as op

import                         fsleyes
import fsleyes.gl           as fslgl
from   .glsl import program as glslprogram
from   .arbp import program as arbpprogram


GLSLShader = glslprogram.GLSLShader
ARBPShader = arbpprogram.ARBPShader


[docs]def getShaderDir(): """Returns the directory in which the ``ARB`` and ``glsl`` shader program source files can be found. A different directory will be returned depending on which OpenGL version is in use. """ if fslgl.GL_COMPATIBILITY == '2.1': subdir = 'gl21' elif fslgl.GL_COMPATIBILITY == '1.4': subdir = 'gl14' return op.join(fsleyes.assetDir, 'gl', subdir)
[docs]def getShaderSuffix(): """Returns the shader program file suffix to use. A different suffix will be returned depending on which OpenGL version is in use. """ if fslgl.GL_COMPATIBILITY == '2.1': return 'glsl' elif fslgl.GL_COMPATIBILITY == '1.4': return 'prog'
[docs]def getVertexShader(prefix): """Returns the vertex shader source for the given GL type (e.g. 'glvolume'). """ return _getShader(prefix, 'vert')
[docs]def getFragmentShader(prefix): """Returns the fragment shader source for the given GL type.""" return _getShader(prefix, 'frag')
[docs]def _getShader(prefix, shaderType): """Returns the shader source for the given GL type and the given shader type ('vert' or 'frag'). """ fname = _getFileName(prefix, shaderType) with open(fname, 'rt', encoding='utf-8') as f: src = f.read() return preprocess(src)
[docs]def _getFileName(prefix, shaderType): """Returns the file name of the shader program for the given GL type and shader type. """ suffix = getShaderSuffix() if shaderType not in ('vert', 'frag'): raise RuntimeError('Invalid shader type: {}'.format(shaderType)) return op.join(getShaderDir(), '{}_{}.{}'.format( prefix, shaderType, suffix))
[docs]def preprocess(src): """'Preprocess' the given shader source. This amounts to searching for lines containing '#pragma include filename', and replacing those lines with the contents of the specified files. """ lines = src.split('\n') lines = [l.strip() for l in lines] pragmas = [] for linei, line in enumerate(lines): if line.startswith('#pragma'): pragmas.append(linei) includes = [] for linei in pragmas: line = lines[linei].split() if len(line) != 3: continue if line[1] != 'include': continue includes.append((linei, line[2])) for linei, fname in includes: fname = op.join(getShaderDir(), fname) with open(fname, 'rt', encoding='utf-8') as f: lines[linei] = f.read() return '\n'.join(lines)