diff --git a/bin/pyglslunit.py b/bin/pyglslunit.py new file mode 100644 index 0000000000..47be9c7fdc --- /dev/null +++ b/bin/pyglslunit.py @@ -0,0 +1,121 @@ +#!/usr/bin/python + +from optparse import OptionParser +import re +import sys + +import pystache + + +ESCAPE_SEQUENCE = { + '\\': '\\\\', + '\n': '\\n', + '\t': '\\t' + } + + +def js_escape(s): + return ''.join(ESCAPE_SEQUENCE.get(c, c) for c in s) + + +def glsl_compress(s, shortNames): + # strip leading whitespace + s = re.sub(r'\A\s+', '', s) + # strip trailing whitespace + s = re.sub(r'\s+\Z', '', s) + # strip multi-line comments + s = re.sub(r'/\*.*?\*/', '', s) + # strip single line comments + s = re.sub(r'//.*?\n', '', s) + # replace multiple whitespace with a single space + s = re.sub(r'\s+', ' ', s) + # remove whitespace between non-word tokens + s = re.sub(r'(\S)\s+([^\w])', r'\1\2', s) + s = re.sub(r'([^\w])\s+(\S)', r'\1\2', s) + # replace original names with short names + for originalName, shortName in shortNames.items(): + s = s.replace(originalName, shortName) + return s + + +def main(argv): + option_parser = OptionParser() + option_parser.add_option('--input') + option_parser.add_option('--output') + option_parser.add_option('--template') + options, args = option_parser.parse_args(argv[1:]) + + context = {} + nextShortName = ord('a') + shortNames = {} + + common, vertex, fragment = [], [], [] + attributes, uniforms, varyings = {}, {}, {} + block = None + for line in open(options.input): + if line.startswith('//!'): + m = re.match(r'//!\s+NAMESPACE=(\S+)\s*\Z', line) + if m: + context['namespace'] = m.group(1) + continue + m = re.match(r'//!\s+CLASS=(\S+)\s*\Z', line) + if m: + context['className'] = m.group(1) + continue + m = re.match(r'//!\s+COMMON\s*\Z', line) + if m: + block = common + continue + m = re.match(r'//!\s+VERTEX\s*\Z', line) + if m: + block = vertex + continue + m = re.match(r'//!\s+FRAGMENT\s*\Z', line) + if m: + block = fragment + continue + else: + if block is None: + assert line.rstrip() == '' + else: + block.append(line) + m = re.match(r'attribute\s+\S+\s+(\S+);\s*\Z', line) + if m: + attribute = m.group(1) + if attribute not in attributes: + shortName = chr(nextShortName) + nextShortName += 1 + attributes[attribute] = {'originalName': attribute, 'shortName': shortName} + shortNames[attribute] = shortName + m = re.match(r'uniform\s+\S+\s+(\S+);\s*\Z', line) + if m: + uniform = m.group(1) + if uniform not in uniforms: + shortName = chr(nextShortName) + nextShortName += 1 + uniforms[uniform] = {'originalName': uniform, 'shortName': shortName} + shortNames[uniform] = shortName + m = re.match(r'varying\s+\S+\s+(\S+);\s*\Z', line) + if m: + varying = m.group(1) + if varying not in varyings: + shortName = chr(nextShortName) + nextShortName += 1 + shortNames[varying] = shortName + + context['getOriginalFragmentSource'] = js_escape(''.join(common + fragment)) + context['getOriginalVertexSource'] = js_escape(''.join(common + vertex)) + context['getFragmentSource'] = glsl_compress(''.join(common + fragment), shortNames) + context['getVertexSource'] = glsl_compress(''.join(common + vertex), shortNames) + context['getAttributes'] = [attributes[a] for a in sorted(attributes.keys())] + context['getUniforms'] = [uniforms[u] for u in sorted(uniforms.keys())] + + if options.output and options.output != '-': + output = open(options.output, 'w') + else: + output = sys.stdout + output.write(pystache.render(open(options.template).read(), context)) + + +if __name__ == '__main__': + sys.exit(main(sys.argv))