diff --git a/Makefile b/Makefile index 75e5104995..24fcfe4cfc 100644 --- a/Makefile +++ b/Makefile @@ -262,12 +262,12 @@ build/ol-debug.js: config/ol-debug.json $(SRC_JS) $(SRC_SHADER_JS) $(SRC_SHADERL @$(STAT_COMPRESSED) /tmp/ol-debug.js.gz @rm /tmp/ol-debug.js.gz -%shader.js: %shader.glsl src/ol/webgl/shader.mustache bin/pyglslunit.py build/timestamps/node-modules-timestamp - @python bin/pyglslunit.py --input $< | ./node_modules/.bin/mustache - src/ol/webgl/shader.mustache > $@ +%shader.js: %shader.glsl src/ol/webgl/shader.mustache tasks/glslunit.js build/timestamps/node-modules-timestamp + @node tasks/glslunit.js --input $< | ./node_modules/.bin/mustache - src/ol/webgl/shader.mustache > $@ -%shader/locations.js: %shader.glsl src/ol/webgl/shaderlocations.mustache bin/pyglslunit.py build/timestamps/node-modules-timestamp +%shader/locations.js: %shader.glsl src/ol/webgl/shaderlocations.mustache tasks/glslunit.js build/timestamps/node-modules-timestamp @mkdir -p $(@D) - @python bin/pyglslunit.py --input $< | ./node_modules/.bin/mustache - src/ol/webgl/shaderlocations.mustache > $@ + @node tasks/glslunit.js --input $< | ./node_modules/.bin/mustache - src/ol/webgl/shaderlocations.mustache > $@ .PHONY: package package: diff --git a/bin/pyglslunit.py b/bin/pyglslunit.py deleted file mode 100644 index de1b887bba..0000000000 --- a/bin/pyglslunit.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/python - -from optparse import OptionParser -import json -import re -import sys - - - -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') - 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, 'rU'): - 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, 'wb') - else: - output = sys.stdout - json.dump(context, output) - - -if __name__ == '__main__': - sys.exit(main(sys.argv)) diff --git a/tasks/glslunit.js b/tasks/glslunit.js new file mode 100644 index 0000000000..236719951c --- /dev/null +++ b/tasks/glslunit.js @@ -0,0 +1,146 @@ +const fs = require('fs'); + +const ESCAPE_SEQUENCE = { + '\\': '\\\\', + '\n': '\\n', + '\t': '\\t' +}; + +function js_escape(s) { + return s.split('').map(function(c) { + return ESCAPE_SEQUENCE[c] || c; + }).join(''); +} + +function glsl_compress(s, shortNames) { + // strip leading whitespace + s = s.replace(/^\s+/g, ''); + // strip trailing whitespace + s = s.replace(/\s+$/g, ''); + // strip multi-line comments + s = s.replace(/\/\*[\s\S]*?\*\//g, ''); + // strip single line comments + s = s.replace(/\/\/.*?\n/, ''); + // replace multiple whitespace with a single space + s = s.replace(/\s+/g, ' '); + // remove whitespace between non-word tokens + s = s.replace(/(\S)\s+([^\w])/g, '$1$2') + .replace(/([^\w])\s+(\S)/g, '$1$2'); + // replace original names with short names + for (var originalName in shortNames) { + s = s.replace(new RegExp(originalName, 'gm'), shortNames[originalName]); + } + return s; +} + +function main(argv) { + var options = {}; + for (var i = 2, ii = argv.length; i < ii; i += 2) { + options[argv[i].replace(/^../, '')] = argv[i + 1]; + } + if (!options.input) { + process.stdout.write('--input option missing\n'); + return 1; + } + + const json = {}; + let nextShortName = 'a'.charCodeAt(0); + const shortNames = {}; + + const attributes = {}; + const uniforms = {}; + const varyings = {}; + const blocks = { + common: '', + vertex: '', + fragment: '' + }; + let block = undefined; + const inFile = fs.readFileSync(options.input, 'utf-8'); + const lines = inFile.split('\n'); + + let m, shortName; + lines.forEach(function(line, i) { + if (line.indexOf('//!') == 0) { + m = line.match(/\/\/!\s+NAMESPACE=(\S+)\s*$/); + if (m) { + json.namespace = m[1]; + return; + } + m = line.match(/\/\/!\s+COMMON\s*$/); + if (m) { + block = 'common'; + return; + } + m = line.match(/\/\/!\s+VERTEX\s*$/); + if (m) { + block = 'vertex'; + return; + } + m = line.match(/\/\/!\s+FRAGMENT\s*$/); + if (m) { + block = 'fragment'; + return; + } + } else { + if (block === undefined) { + if (line.replace(/\s+$/g, '') != '') { + process.stdout.write(`Error parsing ${options.input}\n`); + return; + } + } else { + blocks[block] += line + (i == lines.length - 1 ? '' : '\n'); + } + m = line.match(/attribute\s+\S+\s+(\S+);\s*$/); + if (m) { + const attribute = m[1]; + if (!(attribute in attributes)) { + shortName = String.fromCharCode(nextShortName++); + attributes[attribute] = { + originalName: attribute, + shortName: shortName + }; + shortNames[attribute] = shortName; + } + } + m = line.match(/uniform\s+\S+\s+(\S+);\s*$/); + if (m) { + const uniform = m[1]; + if (!(uniform in uniforms)) { + shortName = String.fromCharCode(nextShortName++); + uniforms[uniform] = { + originalName: uniform, + shortName: shortName + }; + shortNames[uniform] = shortName; + } + } + m = line.match(/varying\s+\S+\s+(\S+);\s*$/); + if (m) { + const varying = m[1]; + if (!(varying in varyings)) { + shortName = String.fromCharCode(nextShortName++); + shortNames[varying] = shortName; + } + } + } + }); + + json.originalFragmentSource = js_escape(blocks.common + blocks.fragment); + json.originalVertexSource = js_escape(blocks.common + blocks.vertex); + json.fragmentSource = glsl_compress(blocks.common + blocks.fragment, shortNames); + json.vertexSource = glsl_compress(blocks.common + blocks.vertex, shortNames); + json.attributes = Object.keys(attributes).map(a => attributes[a]); + json.uniforms = Object.keys(uniforms).map(u => uniforms[u]); + + if (options.output && options.output != '-') { + fs.writeFileSync(options.output, JSON.stringify(json)); + } else { + process.stdout.write(JSON.stringify(json)); + } + return 0; +} + +if (require.main === module) { + process.exit(main(process.argv)); +}