1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
|
# -*- coding: utf-8 -*-
# Copyright (c) Vispy Development Team. All Rights Reserved.
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
import numpy as np
from ...ext.six import string_types
from .shader_object import ShaderObject
VARIABLE_TYPES = ('const', 'uniform', 'attribute', 'varying', 'inout')
class Variable(ShaderObject):
""" Representation of global shader variable
Parameters
----------
name : str
the name of the variable. This string can also contain the full
definition of the variable, e.g. 'uniform vec2 foo'.
value : {float, int, tuple, GLObject}
If given, vtype and dtype are determined automatically. If a
float/int/tuple is given, the variable is a uniform. If a gloo
object is given that has a glsl_type property, the variable is
an attribute and
vtype : {'const', 'uniform', 'attribute', 'varying', 'inout'}
The type of variable.
dtype : str
The data type of the variable, e.g. 'float', 'vec4', 'mat', etc.
"""
_vtype_32_conversion = {'in': 'attribute', 'out': 'varying'}
_vtype_23_conversion = {'attribute': 'in', 'varying': 'out'}
def __init__(self, name, value=None, vtype=None, dtype=None):
super(Variable, self).__init__()
# allow full definition in first argument
if ' ' in name:
fields = name.split(' ')
if len(fields) == 3:
vtype, dtype, name = fields
elif len(fields) == 4 and fields[0] == 'const':
vtype, dtype, name, value = fields
else:
raise ValueError('Variable specifications given by string must'
' be of the form "vtype dtype name" or '
'"const dtype name value".')
if not (isinstance(name, string_types) or name is None):
raise TypeError("Variable name must be string or None.")
self._state_counter = 0
self._name = name
self._vtype = self._vtype_32_conversion.get(vtype, vtype)
self._dtype = dtype
self._value = None
# If vtype/dtype were given at init, then we will never
# try to set these values automatically.
self._type_locked = self._vtype is not None and self._dtype is not None
if value is not None:
self.value = value
if self._vtype and self._vtype not in VARIABLE_TYPES:
raise ValueError('Not a valid vtype: %r' % self._vtype)
@property
def name(self):
""" The name of this variable.
"""
return self._name
@name.setter
def name(self, n):
# Settable mostly to allow automatic setting of varying names
# See ShaderObject.create()
if self._name != n:
self._name = n
self.changed(code_changed=True)
@property
def vtype(self):
""" The type of variable (const, uniform, attribute, or varying).
For in/out variables (GLSL 150+), vtype is 'varying'.
"""
return self._vtype
@property
def dtype(self):
""" The type of data (float, int, vec, mat, ...).
"""
return self._dtype
@property
def value(self):
""" The value associated with this variable.
"""
return self._value
@value.setter
def value(self, value):
if isinstance(value, (tuple, list)) and 1 < len(value) < 5:
vtype = 'uniform'
dtype = 'vec%d' % len(value)
elif isinstance(value, np.ndarray):
if value.ndim == 1 and (1 < len(value) < 5):
vtype = 'uniform'
dtype = 'vec%d' % len(value)
elif value.ndim == 2 and value.shape in ((2, 2), (3, 3), (4, 4)):
vtype = 'uniform'
dtype = 'mat%d' % value.shape[0]
else:
raise ValueError("Cannot make uniform value for %s from array "
"of shape %s." % (self.name, value.shape))
elif np.isscalar(value):
vtype = 'uniform'
if isinstance(value, (float, np.floating)):
dtype = 'float'
elif isinstance(value, (int, np.integer)):
dtype = 'int'
else:
raise TypeError("Unknown data type %r for variable %r" %
(type(value), self))
elif getattr(value, 'glsl_type', None) is not None:
# Note: hasattr() is broken by design--swallows all exceptions!
vtype, dtype = value.glsl_type
else:
raise TypeError("Unknown data type %r for variable %r" %
(type(value), self))
self._value = value
self._state_counter += 1
if self._type_locked:
if dtype != self._dtype or vtype != self._vtype:
raise TypeError('Variable is type "%s"; cannot assign value '
'%r.' % (self.dtype, value))
return
# update vtype/dtype and emit changed event if necessary
changed = False
if self._dtype != dtype:
self._dtype = dtype
changed = True
if self._vtype != vtype:
self._vtype = vtype
changed = True
if changed:
self.changed(code_changed=True, value_changed=True)
@property
def state_id(self):
"""Return a unique ID that changes whenever the state of the Variable
has changed. This allows ModularProgram to quickly determine whether
the value has changed since it was last used."""
return id(self), self._state_counter
def __repr__(self):
return ("<%s \"%s %s %s\" at 0x%x>" % (self.__class__.__name__,
self._vtype, self._dtype,
self.name, id(self)))
def expression(self, names):
return names[self]
def _vtype_for_version(self, version):
"""Return the vtype for this variable, converted based on the GLSL
version.
"""
vtype = self.vtype
if version is None or version[0] == 120:
return self._vtype_32_conversion.get(vtype, vtype)
else:
return self._vtype_23_conversion.get(vtype, vtype)
def definition(self, names, version, shader):
if self.vtype is None:
raise RuntimeError("Variable has no vtype: %r" % self)
if self.dtype is None:
raise RuntimeError("Variable has no dtype: %r" % self)
name = names[self]
vtype = self._vtype_for_version(version)
if vtype == 'const':
return '%s %s %s = %s;' % (vtype, self.dtype, name, self.value)
else:
return '%s %s %s;' % (vtype, self.dtype, name)
class Varying(Variable):
""" Representation of a varying (variables passed from one shader to the
next).
Varyings can inherit their dtype from another Variable, allowing for
more flexibility in composing shaders.
"""
def __init__(self, name, dtype=None):
self._link = None
self._src_func = None
self._dst_func = None
Variable.__init__(self, name, vtype='varying', dtype=dtype)
@property
def value(self):
""" The value associated with this variable.
"""
return self._value
@value.setter
def value(self, value):
if value is not None:
raise TypeError("Cannot assign value directly to varying.")
@property
def dtype(self):
if self._dtype is None:
if self._link is None:
return None
else:
return self._link.dtype
else:
return self._dtype
def link(self, var):
""" Link this Varying to another object from which it will derive its
dtype.
This method is used internally when assigning an attribute to
a varying using syntax ``function[varying] = attr``.
"""
assert self._dtype is not None or hasattr(var, 'dtype')
self._link = var
self.changed()
def invar(self, array=False):
"""Return a varying that defines itself using the same name as this,
but as an `in` variable instead of `out`.
"""
return InVar(self, array=array)
class InVar(Variable):
def __init__(self, var, array=False):
self._var = var
self._array = array
Variable.__init__(self, var.name)
@property
def value(self):
""" The value associated with this variable.
"""
return self._var.value
@value.setter
def value(self, value):
if value is not None:
raise TypeError("Cannot assign value directly to varying.")
@property
def dtype(self):
return self._var.dtype
def definition(self, names, version, shader):
# inherit name from source variable
name = names[self._var]
dtype = self._var.dtype
if version[0] <= 120:
return "varying %s %s;" % (dtype, name)
else:
if self._array:
return "in %s %s[];" % (dtype, name)
else:
return "in %s %s;" % (dtype, name)
def expression(self, names):
return names[self._var]
|