OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python |
| 2 import optparse |
| 3 import os |
| 4 import re |
| 5 import subprocess |
| 6 import sys |
| 7 |
| 8 # This script takes an o3d cg shader from standard input and does the following: |
| 9 # |
| 10 # * it extracts entry points to vertex and fragment shaders as specified by |
| 11 # VertexShaderEntryPoint and PixelShaderEntryPoint instructions; |
| 12 # |
| 13 # * renames NORMAL, TANGENT{,1} and BINORMAL{,1} attributes to ATTR8-12; |
| 14 # |
| 15 # * runs cgc with glslv and glslf profiles with those entry points; |
| 16 # |
| 17 # * renames attributes and uniforms back to their orignal names; |
| 18 # |
| 19 # * changes 'uniform vecN var[N]' to 'uniform matN var'; |
| 20 # |
| 21 # * renames gl_Vertex and gl_MultiTexCoordN to position and texcoordsN |
| 22 # respectively and adds attribute declarations; |
| 23 # |
| 24 # * prints the results to standard output, separating them with SplitMarker |
| 25 # instruction and keeping the MatrixLoadOrder instruction as is. |
| 26 |
| 27 |
| 28 CGC = '/usr/bin/cgc' |
| 29 |
| 30 # cgc complains about TANGENT1 and BINORMAL1 semantics, so we hack it by |
| 31 # replacing standard semantics with ATTR8-ATTR12 and then renaming them back to |
| 32 # their original names. |
| 33 ATTRIBUTES_TO_SEMANTICS = dict( |
| 34 attr8 = 'normal', |
| 35 attr9 = 'tangent', |
| 36 attr10 = 'binormal', |
| 37 attr11 = 'tangent1', |
| 38 attr12 = 'binormal1') |
| 39 |
| 40 |
| 41 def get_input_mapping(header): |
| 42 ret = dict() |
| 43 for l in header.splitlines(): |
| 44 if not l.startswith('//var'): |
| 45 continue |
| 46 old_name_and_type, semantic, new_name, _, _ = l.split(' : ') |
| 47 if '[' in new_name: |
| 48 new_name = new_name[:new_name.index('[')] |
| 49 if new_name.startswith('$'): |
| 50 new_name = new_name[1:] |
| 51 ret[new_name] = (semantic.lower() if semantic |
| 52 else old_name_and_type.split(' ')[2]) |
| 53 return ret |
| 54 |
| 55 |
| 56 def fix_glsl_body(body, input_mapping): |
| 57 # Change uniform names back to original. |
| 58 for match in re.findall(r'(?m)^uniform (?:\w+) (\w+)', body): |
| 59 body = re.sub(r'\b%s\b' % match, input_mapping[match], body) |
| 60 |
| 61 # Change attribute names back to original. |
| 62 for match in re.findall(r'(?m)^attribute (?:\w+) (\w+)', body): |
| 63 attr_name = input_mapping[match] |
| 64 assert attr_name.startswith('$vin.') |
| 65 orig_name = ATTRIBUTES_TO_SEMANTICS[attr_name[len('$vin.'):]] |
| 66 body = re.sub(r'\b%s\b' % match, orig_name, body) |
| 67 |
| 68 # Change vecN blah[N]; to matN blah; |
| 69 body = re.sub(r'(?m)^uniform vec(\d) (\w+)\[\1\];', r'uniform mat\1 \2;', |
| 70 body) |
| 71 |
| 72 attributes = [] |
| 73 if 'gl_Vertex' in body: |
| 74 # Change gl_Vertex to position and add attribute declaration. |
| 75 body = re.sub(r'\bgl_Vertex\b', 'position', body) |
| 76 attributes.append('attribute vec4 position;') |
| 77 |
| 78 for n in xrange(8): |
| 79 if 'gl_MultiTexCoord%d' % n in body: |
| 80 # Change gl_MultiTexCoordN (0<=N<=7) to texcoordsN and add attribute |
| 81 # declaration. |
| 82 body = re.sub(r'\bgl_MultiTexCoord%d\b' % n, 'texcoords%d' % n, body) |
| 83 attributes.append('attribute vec4 texcoords%d;' % n) |
| 84 |
| 85 # ATTRIBUTES_TO_SEMANTICS should have taken care of normals. |
| 86 assert 'gl_Normal' not in body |
| 87 |
| 88 return '\n'.join(attributes) + '\n\n' + body |
| 89 |
| 90 |
| 91 def fix_glsl(glsl_shader): |
| 92 header, body = re.split(r'\n\n', glsl_shader, 1) |
| 93 assert all(l.startswith('//') for l in header.splitlines()) |
| 94 input_mapping = get_input_mapping(header) |
| 95 return header + '\n\n' + fix_glsl_body(body, input_mapping) |
| 96 |
| 97 |
| 98 def cg_rename_attributes(cg_shader): |
| 99 for new, old in ATTRIBUTES_TO_SEMANTICS.iteritems(): |
| 100 cg_shader = re.sub(r'\b%s\b' % old.upper(), new.upper(), cg_shader) |
| 101 return cg_shader |
| 102 |
| 103 |
| 104 def cg_to_glsl(cg_shader): |
| 105 cg_shader = cg_rename_attributes(cg_shader) |
| 106 |
| 107 vertex_entry = re.search(r'#o3d\s+VertexShaderEntryPoint\s+(\w+)', |
| 108 cg_shader).group(1) |
| 109 p = subprocess.Popen(CGC+' -profile glslv -entry %s' % vertex_entry, |
| 110 shell=True, |
| 111 stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 112 glsl_vertex, err_v = p.communicate(cg_shader) |
| 113 |
| 114 fragment_entry = re.search(r'#o3d\s+PixelShaderEntryPoint\s+(\w+)', |
| 115 cg_shader).group(1) |
| 116 p = subprocess.Popen(CGC+' -profile glslf -entry %s' % fragment_entry, |
| 117 shell=True, |
| 118 stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 119 glsl_fragment, err_f = p.communicate(cg_shader) |
| 120 |
| 121 log = ( |
| 122 '// glslv profile log:\n' + |
| 123 '\n'.join('// ' + l for l in err_v.splitlines()) + '\n\n' |
| 124 '// glslf profile log:\n' + |
| 125 '\n'.join('// ' + l for l in err_f.splitlines())) + '\n' |
| 126 |
| 127 return glsl_vertex, glsl_fragment, log |
| 128 |
| 129 |
| 130 def get_matrixloadorder(cg_shader): |
| 131 return re.search(r'(?m)^.*#o3d\s+MatrixLoadOrder\b.*$', cg_shader).group(0) |
| 132 |
| 133 |
| 134 def check_cg(): |
| 135 if not os.path.exists(CGC): |
| 136 print >>sys.stderr, CGC+' is not found, use --cgc option to specify its' |
| 137 print >>sys.stderr, 'location. You may need to install nvidia cg toolkit.' |
| 138 print >>sys.stderr, 'In Ubuntu distribution it can be done by running:' |
| 139 print >>sys.stderr, ' "apt-get install nvidia-cg-toolkit"' |
| 140 sys.exit(1) |
| 141 |
| 142 |
| 143 def main(cg_shader): |
| 144 matrixloadorder = get_matrixloadorder(cg_shader) |
| 145 glsl_vertex, glsl_fragment, log = cg_to_glsl(cg_shader) |
| 146 |
| 147 print log |
| 148 print fix_glsl(glsl_vertex) |
| 149 print |
| 150 print '// #o3d SplitMarker' |
| 151 print get_matrixloadorder(cg_shader).strip() |
| 152 print |
| 153 print fix_glsl(glsl_fragment) |
| 154 |
| 155 |
| 156 if __name__ == '__main__': |
| 157 cmdline_parser = optparse.OptionParser() |
| 158 cmdline_parser.add_option('--cgc', dest='CGC', default='/usr/bin/cgc', |
| 159 help='path to cgc [default: %default]') |
| 160 options, args = cmdline_parser.parse_args() |
| 161 CGC = options.CGC |
| 162 check_cg() |
| 163 |
| 164 try: |
| 165 input = sys.stdin.read() |
| 166 except KeyboardInterrupt: |
| 167 input = None |
| 168 |
| 169 if not input: |
| 170 cmdline_parser.print_help() |
| 171 else: |
| 172 main(input) |
OLD | NEW |