import ast
from shaderdef.ast_util import parse_source
from shaderdef.glsl_var import GlslVar
from shaderdef.glsl_types import vec4
[docs]def snake_case(string):
output = ''
first = True
for char in string:
lower = char.lower()
if char != lower:
if first:
first = False
else:
output += '_'
output += lower
return output
def _declare_block(block_type, block_name, instance_name, members,
array):
# TODO
assert len(members) != 0
assert block_type in ('in', 'out', 'uniform')
array_string = ''
if array is not None:
if isinstance(array, bool):
array_string = '[]'
else:
raise NotImplementedError('only unsized arrays are supported')
yield '{} {} {{'.format(block_type, block_name)
for member in members:
# TODO
assert not member.name.startswith('gl_')
yield ' ' + member.declare()
yield '}} {}{};'.format(instance_name, array_string)
# https://www.opengl.org/wiki/Interface_Block_(GLSL)
[docs]class ShaderInterface(object):
def __init__(self, **kwargs):
pass
@classmethod
[docs] def get_vars(cls):
# ast is used here instead of inspecting the attributes
# directly because currently most of the types are just
# aliases of GlslType rather than subclasses
src = parse_source(cls)
cls_node = src.body[0]
for item in cls_node.body:
if isinstance(item, ast.Assign):
name = item.targets[0].id
# Ignore builtins
if name.startswith('gl_'):
continue
if not isinstance(item.value, ast.Call):
raise TypeError('member is not a constructor: {}'
.format(ast.dump(item)))
gtype = item.value.func.id
interp = None
if len(item.value.args) == 1:
interp = item.value.args[0].id
yield GlslVar(name, gtype, interpolation=interp)
@classmethod
def _declare_block(cls, instance_name, block_type, array=None):
members = list(cls.get_vars())
if len(members) == 0:
return []
else:
return list(_declare_block(block_type, cls.block_name(),
instance_name, members, array))
@classmethod
@classmethod
[docs] def declare_output_block(cls, array=None):
instance_name = snake_case(cls.instance_name())
return cls._declare_block(instance_name, 'out', array=array)
@classmethod
[docs] def block_name(cls):
return cls.__name__
@classmethod
[docs] def instance_name(cls):
return snake_case(cls.__name__)
[docs]class AttributeBlock(ShaderInterface):
# For whatever reason GLSL doesn't allow attributes to be
# aggregated into an interface block
@classmethod
[docs]class FragmentShaderOutputBlock(ShaderInterface):
# As with attributes, blocks aren't allowed here
@classmethod
[docs] def declare_output_block(cls, array=None):
if array is not None:
raise NotImplementedError('fs output arrays not implemented')
# TODO(nicholasbishop): dedup
location = 0
for member in cls.get_vars():
# TODO(nicholasbishop): correctly handle type size when
# incrementing location
yield member.declare_output(location)
location += 1
# TODO, builtin
[docs]class GlGsIn(ShaderInterface):
gl_position = vec4()