2 from xml.etree import ElementTree as ET
3 from OpenGL.GL import *
4 from OpenGL.GL import shaders
7 SCALE_METHOD_FIXED = "fixed"
8 SCALE_METHOD_INPUT_SCALE = "input"
9 SCALE_METHOD_OUTPUT_SCALE = "output"
10 SCALE_METHOD_NONE = None
13 ELEM_SHADER = "shader"
14 ELEM_VERTEX = "vertex"
15 ELEM_FRAGMENT = "fragment"
17 ATTR_LANGUAGE = "language"
18 ATTR_LANGUAGE_GLSL = "GLSL"
19 ATTR_FILTER = "filter"
20 ATTR_FILTER_NEAREST = "nearest"
21 ATTR_FILTER_LINEAR = "linear"
22 ATTR_FILTER_DEFAULT = ATTR_FILTER_LINEAR
24 ATTR_SIZE_X = "size_x"
25 ATTR_SIZE_Y = "size_y"
27 ATTR_SCALE_X = "scale_x"
28 ATTR_SCALE_Y = "scale_y"
29 ATTR_OUTSCALE = "outscale"
30 ATTR_OUTSCALE_X = "outscale_x"
31 ATTR_OUTSCALE_Y = "outscale_y"
34 class ShaderReaderException(Exception):
38 class ShaderPass(object):
40 def __init__(self, elements):
43 if elem.tag == ELEM_VERTEX:
45 shaders.compileShader(elem.text, GL_VERTEX_SHADER)
49 shaders.compileShader(elem.text, GL_FRAGMENT_SHADER)
52 self.programID = shaders.compileProgram(*parts)
54 fragmentElem = elements[-1]
56 self.filterMethod = fragmentElem.get(ATTR_FILTER, ATTR_FILTER_DEFAULT)
57 if self.filterMethod not in (ATTR_FILTER_NEAREST, ATTR_FILTER_LINEAR):
58 raise ShaderReaderException("'filter' attribute should be "
59 "'nearest' or 'linear', not %r" % (self.filterMethod,))
61 self.horizontalScaleMethod = SCALE_METHOD_NONE
62 self.horizontalScaleValue = None
63 self.verticalScaleMethod = SCALE_METHOD_NONE
64 self.verticalScaleValue = None
66 self._set_scale(fragmentElem, ATTR_SIZE, int, True,
68 self._set_scale(fragmentElem, ATTR_SIZE, int, False,
70 self._set_scale(fragmentElem, ATTR_SIZE_X, int, True,
72 self._set_scale(fragmentElem, ATTR_SIZE_Y, int, False,
74 self._set_scale(fragmentElem, ATTR_SCALE, float, True,
75 SCALE_METHOD_INPUT_SCALE)
76 self._set_scale(fragmentElem, ATTR_SCALE, float, False,
77 SCALE_METHOD_INPUT_SCALE)
78 self._set_scale(fragmentElem, ATTR_SCALE_X, float, True,
79 SCALE_METHOD_INPUT_SCALE)
80 self._set_scale(fragmentElem, ATTR_SCALE_Y, float, False,
81 SCALE_METHOD_INPUT_SCALE)
82 self._set_scale(fragmentElem, ATTR_OUTSCALE, float, True,
83 SCALE_METHOD_OUTPUT_SCALE)
84 self._set_scale(fragmentElem, ATTR_OUTSCALE, float, False,
85 SCALE_METHOD_OUTPUT_SCALE)
86 self._set_scale(fragmentElem, ATTR_OUTSCALE_X, float, True,
87 SCALE_METHOD_OUTPUT_SCALE)
88 self._set_scale(fragmentElem, ATTR_OUTSCALE_Y, float, False,
89 SCALE_METHOD_OUTPUT_SCALE)
91 def _set_scale(self, elem, fieldName, parser, horiz, method):
92 if fieldName not in elem.attrib:
96 value = parser(elem.attrib[fieldName])
98 raise ShaderReaderException("Field %s should have a value "
99 "compatible with %r, not %r" % (
100 fieldName, parser, elem.attrib[fieldName],
105 if self.horizontalScaleValue is not None:
106 raise ShaderReaderException("Can't apply attribute %s "
107 "because this shader pass already sets a "
108 "horizontal scaling method.")
109 self.horizontalScaleMethod = method
110 self.horizontalScaleValue = value
113 if self.verticalScaleValue is not None:
114 raise ShaderReaderException("Can't apply attribute %s "
115 "because this shader pass already sets a "
116 "vertical scaling method.")
117 self.verticalScaleMethod = method
118 self.verticalScaleValue = value
120 def calculateFramebufferSize(self, inputW, inputH, finalW, finalH):
121 if self.horizontalScaleMethod == SCALE_METHOD_INPUT_SCALE:
122 outputW = inputW * self.horizontalScaleValue
123 elif self.horizontalScaleMethod == SCALE_METHOD_OUTPUT_SCALE:
124 outputW = finalW * self.horizontalScaleValue
125 elif self.horizontalScaleMethod == SCALE_METHOD_FIXED:
126 outputW = self.horizontalScaleValue
130 if self.verticalScaleMethod == SCALE_METHOD_INPUT_SCALE:
131 outputH = inputH * self.verticalScaleValue
132 elif self.verticalScaleMethod == SCALE_METHOD_OUTPUT_SCALE:
133 outputH = finalH * self.verticalScaleValue
134 elif self.verticalScaleMethod == SCALE_METHOD_FIXED:
135 outputH = self.verticalScaleValue
139 return outputW, outputH
141 def requiresImplicitPass(self):
142 if self.horizontalScaleMethod != SCALE_METHOD_NONE:
144 if self.verticalScaleMethod != SCALE_METHOD_NONE:
149 def parse_shader(data):
150 root = ET.fromstring(data)
152 if root.tag != ELEM_SHADER:
153 raise ShaderReaderException("Root element of XML shader should be "
156 if root.get(ATTR_LANGUAGE) != ATTR_LANGUAGE_GLSL:
157 raise ShaderReaderException("Currently, only GLSL shaders supported.")
162 for elem in root.getchildren():
163 if elem.tag not in (ELEM_VERTEX, ELEM_FRAGMENT):
166 if elem.tag == ELEM_VERTEX:
168 raise ShaderReaderException("Found a new <vertex/> element "
169 "before the previous shader pass was complete. This "
170 "shader pass is malformed.")
172 shaderParts.append(elem)
176 # We've got a fragment shader, ending this shader pass.
177 shaderParts.append(elem)
178 shaderPasses.append(ShaderPass(shaderParts))
183 raise ShaderReaderException("No shaders found in the shader file.")
188 if __name__ == "__main__":
190 from pprint import pprint
191 with open(sys.argv[1], "r") as handle:
194 pprint(parse_shader(data))