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"
23 ATTR_SIZE_X = "size_x"
24 ATTR_SIZE_Y = "size_y"
26 ATTR_SCALE_X = "scale_x"
27 ATTR_SCALE_Y = "scale_y"
28 ATTR_OUTSCALE = "outscale"
29 ATTR_OUTSCALE_X = "outscale_x"
30 ATTR_OUTSCALE_Y = "outscale_y"
33 class ShaderReaderException(Exception):
37 class ShaderPass(object):
39 def __init__(self, elements):
42 if elem.tag == ELEM_VERTEX:
44 shaders.compileShader(elem.text, GL_VERTEX_SHADER)
48 shaders.compileShader(elem.text, GL_FRAGMENT_SHADER)
51 self.programID = shaders.compileProgram(*parts)
53 fragmentElem = elements[-1]
55 self.filterMethod = fragmentElem.get(ATTR_FILTER, ATTR_FILTER_NEAREST)
56 if self.filterMethod not in (ATTR_FILTER_NEAREST, ATTR_FILTER_LINEAR):
57 raise ShaderReaderException("'filter' attribute should be "
58 "'nearest' or 'linear', not %r" % (self.filterMethod,))
60 self.horizontalScaleMethod = SCALE_METHOD_NONE
61 self.horizontalScaleValue = None
62 self.verticalScaleMethod = SCALE_METHOD_NONE
63 self.verticalScaleValue = None
65 self._set_scale(fragmentElem, ATTR_SIZE, int, True,
67 self._set_scale(fragmentElem, ATTR_SIZE, int, False,
69 self._set_scale(fragmentElem, ATTR_SIZE_X, int, True,
71 self._set_scale(fragmentElem, ATTR_SIZE_Y, int, False,
73 self._set_scale(fragmentElem, ATTR_SCALE, float, True,
74 SCALE_METHOD_INPUT_SCALE)
75 self._set_scale(fragmentElem, ATTR_SCALE, float, False,
76 SCALE_METHOD_INPUT_SCALE)
77 self._set_scale(fragmentElem, ATTR_SCALE_X, float, True,
78 SCALE_METHOD_INPUT_SCALE)
79 self._set_scale(fragmentElem, ATTR_SCALE_Y, float, False,
80 SCALE_METHOD_INPUT_SCALE)
81 self._set_scale(fragmentElem, ATTR_OUTSCALE, float, True,
82 SCALE_METHOD_OUTPUT_SCALE)
83 self._set_scale(fragmentElem, ATTR_OUTSCALE, float, False,
84 SCALE_METHOD_OUTPUT_SCALE)
85 self._set_scale(fragmentElem, ATTR_OUTSCALE_X, float, True,
86 SCALE_METHOD_OUTPUT_SCALE)
87 self._set_scale(fragmentElem, ATTR_OUTSCALE_Y, float, False,
88 SCALE_METHOD_OUTPUT_SCALE)
90 def _set_scale(self, elem, fieldName, parser, horiz, method):
91 if fieldName not in elem.attrib:
95 value = parser(elem.attrib[fieldName])
97 raise ShaderReaderException("Field %s should have a value "
98 "compatible with %r, not %r" % (
99 fieldName, parser, elem.attrib[fieldName],
104 if self.horizontalScaleValue is not None:
105 raise ShaderReaderException("Can't apply attribute %s "
106 "because this shader pass already sets a "
107 "horizontal scaling method.")
108 self.horizontalScaleMethod = method
109 self.horizontalScaleValue = value
112 if self.verticalScaleValue is not None:
113 raise ShaderReaderException("Can't apply attribute %s "
114 "because this shader pass already sets a "
115 "vertical scaling method.")
116 self.verticalScaleMethod = method
117 self.verticalScaleValue = value
119 def calculateFramebufferSize(self, inputW, inputH, finalW, finalH):
120 if self.horizontalScaleMethod == SCALE_METHOD_INPUT_SCALE:
121 outputW = inputW * self.horizontalScaleValue
122 elif self.horizontalScaleMethod == SCALE_METHOD_OUTPUT_SCALE:
123 outputW = finalW * self.horizontalScaleValue
124 elif self.horizontalScaleMethod == SCALE_METHOD_FIXED:
125 outputW = self.horizontalScaleValue
129 if self.verticalScaleMethod == SCALE_METHOD_INPUT_SCALE:
130 outputH = inputH * self.verticalScaleValue
131 elif self.verticalScaleMethod == SCALE_METHOD_OUTPUT_SCALE:
132 outputH = finalH * self.verticalScaleValue
133 elif self.verticalScaleMethod == SCALE_METHOD_FIXED:
134 outputH = self.verticalScaleValue
138 return outputW, outputH
140 def requiresImplicitPass(self):
141 if self.horizontalScaleMethod != SCALE_METHOD_NONE:
143 if self.verticalScaleMethod != SCALE_METHOD_NONE:
148 def parse_shader(data):
149 root = ET.fromstring(data)
151 if root.tag != ELEM_SHADER:
152 raise ShaderReaderException("Root element of XML shader should be "
155 if root.get(ATTR_LANGUAGE) != ATTR_LANGUAGE_GLSL:
156 raise ShaderReaderException("Currently, only GLSL shaders supported.")
161 for elem in root.getchildren():
162 if elem.tag not in (ELEM_VERTEX, ELEM_FRAGMENT):
165 if elem.tag == ELEM_VERTEX:
167 raise ShaderReaderException("Found a new <vertex/> element "
168 "before the previous shader pass was complete. This "
169 "shader pass is malformed.")
171 shaderParts.append(elem)
175 # We've got a fragment shader, ending this shader pass.
176 shaderParts.append(elem)
177 shaderPasses.append(ShaderPass(shaderParts))
182 raise ShaderReaderException("No shaders found in the shader file.")
187 if __name__ == "__main__":
189 from pprint import pprint
190 with open(sys.argv[1], "r") as handle:
193 pprint(parse_shader(data))