def getPath(iconName): folder = FilePath(os.path.dirname(__file__)) for fmt in FORMATS: path = folder.join(iconName + '.' + fmt) if path.exists(): return path raise Exception('Icon not found: %s' % iconName)
def __openProject(self, path): setCurrentProjectFilePath(FilePath(path)) self.__sceneList.projectOpened() self.__shotsManager.projectOpened() self._timer.projectOpened() self.__models.loadFromProject() self.__modelsOutliner.reset() self.__modeler.viewport.loadState()
def saveStaticTextures(self): exportDir = QFileDialog.getExistingDirectory(None, 'Choose destination folder to save static textures as .png files.', '.') if not exportDir: return for passData in self._scene.passes: if passData.realtime: continue for index, cbo in enumerate(self._scene.colorBuffers[passData.targetBufferId]): cbo.save(FilePath(os.path.join(exportDir, '{}{}.png'.format(passData.name, index))))
def paintGL(self): newTime = time.time() deltaTime = newTime - self._prevTime # work around double repaint events collecting in the queue if deltaTime == 0.0: return self._prevTime = newTime width, height = self.calculateAspect(self.width(), self.height()) viewport = (int((self.width() - width) * 0.5), int((self.height() - height) * 0.5), width, height) if self._scene: uniforms = self._animator.evaluate(self._timer.time) textureUniforms = self._animator.additionalTextures(self._timer.time) cameraData = self._cameraData scene = self._scene modifier = currentProjectDirectory().join('animationprocessor.py') if modifier.exists(): beats = self._timer.time execfile(modifier, globals(), locals()) for name in self._textures: uniforms[name] = self._textures[name]._id self._scene.drawToScreen(self._timer.beatsToSeconds(self._timer.time), self._timer.time, uniforms, viewport, additionalTextureUniforms=textureUniforms) else: # no scene active, time cursor outside any enabled shots? global _noSignalImage if _noSignalImage is None: _noSignalImage = loadImage(FilePath(__file__).parent().join('icons', 'nosignal.png')) glDisable(GL_DEPTH_TEST) if _noSignalImage: glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) Scene.drawColorBufferToScreen(_noSignalImage, viewport) glDisable(GL_BLEND) if self.__overlays: image = self.__overlays.colorBuffer() if image: color = (self.__overlays.overlayColor().red() / 255.0, self.__overlays.overlayColor().green() / 255.0, self.__overlays.overlayColor().blue() / 255.0, self.__overlays.overlayColor().alpha() / 255.0) glDisable(GL_DEPTH_TEST) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) Scene.drawColorBufferToScreen(image, viewport, color) glDisable(GL_BLEND)
def __onNewProject(self): currentPath = self.__changeProjectHelper('Creating new project') if currentPath is None: return res = FileDialog.getSaveFileName(self, 'Create new project', currentPath, 'Project folder') if not res: return shutil.copytree(DEFAULT_PROJECT, res, ignore=lambda p, f: [] if os.path.basename(p).lower() == 'Scenes' else [n for n in f if os.path.splitext(n)[-1].lower() in IGNORED_EXTENSIONS]) projectFile = FilePath(res).join(os.path.basename(res) + PROJ_EXT) projectFile.ensureExists() self.__openProject(projectFile)
def export(self, path): filePath = FilePath(path) filePath = filePath.join("%s.glsl" % self._name) filePath.ensureExists() str = self.getFieldFragmentShaderText() with filePath.edit() as fh: fh.write(str) return
def readCameraData(self): if self.__cameraData is None: userFile = FilePath(currentProjectFilePath() + '.user') xCamera = None if userFile.exists(): xRoot = parseXMLWithIncludes(userFile) for xSub in xRoot: if xSub.attrib['name'] == self.__filePath.name(): xCamera = xSub break if xCamera is None: # legacy support xCamera = parseXMLWithIncludes(self.__filePath) self.__cameraData = CameraTransform( *[float(x) for x in xCamera.attrib['camera'].split(',')]) return self.__cameraData
def initializeGL(self): print(glGetString(GL_VERSION)) glEnable(GL_DEPTH_TEST) glDepthFunc(GL_LEQUAL) # glDepthMask(GL_TRUE) IMAGE_EXTENSIONS = '.png', '.bmp', '.tga' textureFolder = FilePath(__file__).join('..', 'Textures').abs() if textureFolder.exists(): for texture in textureFolder.iter(): if texture.ext() in IMAGE_EXTENSIONS: self._textures[texture.name()] = loadImage( textureFolder.join(texture)) self._prevTime = time.time() self._timer.kick()
def __changeProjectHelper(self, title): """ Utility that shows a dialog if we're changing projects with potentially unsaved changes. Returns the current project directory, or the current working directory if no such project. """ currentPath = FilePath(os.getcwd()) project = currentProjectFilePath() if project is not None: # propose to save near current project dir = project.parent() if dir.exists(): currentPath = dir # check if unsaved changes if QMessageBox.No == QMessageBox.warning(self, title, 'Any unsaved changes will be lost. Continue?', QMessageBox.Yes | QMessageBox.No): return return currentPath
def initializeGL(self): # TODO: Handle re-parenting of the widget in PySide6, it invalidates the context so we need to dirty every cache, or maybe just setCentralWidget and not dock the 3D view, # but it is a fundamental dual monitor or beamer feature so we might just have to deal with it. Lazy choice on Qt's part though, we have to reload EVERY model and texture and buffer. print(glGetString(GL_VERSION)) glEnable(GL_DEPTH_TEST) glDepthFunc(GL_LEQUAL) # glDepthMask(GL_TRUE) IMAGE_EXTENSIONS = '.png', '.bmp', '.tga' textureFolder = FilePath(__file__).join('..', 'Textures').abs() if textureFolder.exists(): for texture in textureFolder.iter(): if texture.ext() in IMAGE_EXTENSIONS: self._textures[texture.name()] = loadImage(textureFolder.join(texture)) self._prevTime = time.time() self._timer.kick() if qt_wrapper == 'PySide6': SceneView.screenFBO = self.defaultFramebufferObject()
def _reload(self, path): if path: path = FilePath(path) time.sleep(0.01) if not path.exists(): # the scene has been deleted, stop watching it return self.fileSystemWatcher_scene.addPath(path) self.passes = _deserializePasses(self.__filePath) self.fileSystemWatcher = FileSystemWatcher() self.fileSystemWatcher.fileChanged.connect(self._rebuild) watched = set() for passData in self.passes: newStitches = (set(passData.vertStitches) | set(passData.fragStitches)) - watched if newStitches: self.fileSystemWatcher.addPaths(list(newStitches)) watched |= newStitches self._rebuild(None) self.__cameraData = None
def _deserializeSceneShots(sceneName): sceneFile = currentScenesDirectory().join(sceneName.ensureExt(SCENE_EXT)) xScene = parseXMLWithIncludes(sceneFile) for xShot in xScene: name = xShot.attrib['name'] start = float(xShot.attrib['start']) end = float(xShot.attrib['end']) speed = float(xShot.attrib.get( 'speed', 1.0)) # using get for legacy file support preroll = float(xShot.attrib.get('preroll', 0.0)) curves = OrderedDict() textures = OrderedDict() for xEntry in xShot: if xEntry.tag.lower() == 'channel': curveName = xEntry.attrib['name'] keys = [] if xEntry.text: keys = xEntry.text.split(',') curve = Curve() for i in range(0, len(keys), 8): curve.addKeyWithTangents( tangentBroken=int(keys[i + 6]), tangentMode=int(keys[i + 7]), *[float(x) for x in keys[i:i + 6]]) curves[curveName] = curve if xEntry.tag.lower() == 'texture': textures[xEntry.attrib['name']] = FilePath( xEntry.attrib['path']) shot = Shot(name, sceneName, start, end, curves, textures, speed, preroll) if 'enabled' in xShot.attrib: shot.enabled = xShot.attrib['enabled'] == str(True) yield shot
class Overlays(QWidget): _overlayDir = FilePath(__file__).abs().parent() _overlayNames = ['None'] + list( sorted( os.path.splitext(x)[0] for x in _overlayDir.iter() if x.endswith('.png'))) _overlayCache = {} changed = pyqtSignal() def __init__(self): super(Overlays, self).__init__() l = hlayout() self.setLayout(l) enm = EnumBox(Overlays._overlayNames) enm.setValue(self.overlayIndex()) l.addWidget(enm) enm.valueChanged.connect(self.setOverlayIndex) l.addWidget(enm) clr = ColorBox(self.overlayColor()) l.addWidget(clr) clr.valueChanged.connect(self.setOverlayColor) l.addWidget(clr) def colorBuffer(self): idx = self.overlayIndex() if idx <= 0: return img = Overlays._overlayCache.get(idx, None) if img: return img fpath = Overlays._overlayDir.join(Overlays._overlayNames[idx] + '.png') if not fpath.exists(): return img = loadImage(fpath) Overlays._overlayCache[idx] = img return img def overlayIndex(self): """ :rtype: int """ return gSettings.value('overlayIndex', 0) def setOverlayIndex(self, index): """ :type index: int """ gSettings.setValue('overlayIndex', index) self.changed.emit() def overlayColor(self): """ :rtype: QColor """ return QColor( gSettings.value('overlayColor', qRgba(255, 255, 255, 255))) def setOverlayColor(self, color): """ :type color: QColor """ gSettings.setValue('overlayColor', color.rgba()) self.changed.emit()
def run(): shots = [] scenes = [] scenesDir = currentScenesDirectory() for scenePath in scenesDir.iter(join=True): if not scenePath.hasExt(SCENE_EXT): continue sceneDir = FilePath(scenePath.strip()).stripExt() xScene = parseXMLWithIncludes(scenePath) templatePath = scenesDir.join(scenesDir, xScene.attrib['template']) templateDir = templatePath.stripExt() xTemplate = Template(templatePath) scene = [] for xPass in xTemplate: stitchIds = [] uniforms = {} for xSection in xPass: baseDir = sceneDir if xSection.tag in ('global', 'shared'): baseDir = templateDir shaderFile = baseDir.join(xSection.attrib['path']).abs() stitchIds.append(text.addFile(shaderFile)) for xUniform in xSection: name = xUniform.attrib['name'] values = [ float(x.strip()) for x in xUniform.attrib['value'].split(',') ] uniforms[text.addString(name)] = len( values), floats.addFloats(values, name) programId = shaders.fromStitches(stitchIds) buffer = int(xPass.attrib.get('buffer', -1)) outputs = int(xPass.attrib.get('outputs', 1)) size = int(xPass.attrib.get('size', 0)) width = int(xPass.attrib.get('width', size)) height = int(xPass.attrib.get('height', size)) factor = int(xPass.attrib.get('factor', 1)) static = int(xPass.attrib.get('static', 0)) is3d = int(xPass.attrib.get('is3d', 0)) if buffer != -1: buffer = framebuffers.add(buffer, outputs, width, height, factor, static, is3d) i = 0 key = 'input%s' % i inputs = [] while key in xPass.attrib: v = xPass.attrib[key] if '.' in v: a, b = v.split('.') else: a, b = v, 0 inputs.append((int(a), int(b))) i += 1 key = 'input%s' % i scene.append(passes.add(programId, buffer, inputs, uniforms)) sceneIndex = len(scenes) scenes.append(len(scene)) scenes += scene for xShot in xScene: if xShot.attrib.get('enabled', 'True') == 'False': continue animations = {} for xChannel in xShot: uname = xChannel.attrib['name'] n = uname x = 0 if '.' in uname: n, x = uname.rsplit('.', 1) x = 'xyzw'.index(x) n = text.addString(n) if n not in animations: animations[n] = [] if not xChannel.text: keyframes = [] else: keyframes = [] for i, v in enumerate( float(v.strip()) for v in xChannel.text.split(',')): j = i % 8 if j == 0 or j == 4 or j > 5: continue if j == 5: # out tangent y if v == float( 'inf' ): # stepped tangents are implemented as out tangentY = positive infinity v = 'FLT_MAX' keyframes.append(v) assert len(keyframes) / 4.0 == int(len(keyframes) / 4), len(keyframes) while len(animations[n]) <= x: animations[n].append(None) assert animations[n][x] is None animations[n][x] = floats.addFloats(keyframes), len(keyframes) for channelStack in animations.values(): # TODO we can not / do not check if the channelStack length matches the uniform dimensions inside the shader (e.g. are we sure we're not gonna call glUniform2f for a vec3?) assert None not in channelStack, 'Animation provided for multiple channels but there is one missing (Y if a vec3 or also Z if a vec4).' shots.append((float(xShot.attrib['start']), float(xShot.attrib['end']), sceneIndex, animations)) # sort shots by start time def _serializeShots(shots): shots.sort(key=lambda x: x[0]) shotTimesStart = floats.addFloats( [x for shot in shots for x in (shot[0], shot[1])]) yield '\n\n__forceinline int shotAtBeats(float beats, float& localBeats)\n{\n' if len(shots) == 1: yield '\tlocalBeats = beats - gFloatData[%s];\n' % shotTimesStart yield '\treturn 0;\n' else: yield '\tint shotTimeCursor = 0;\n' yield '\tdo\n\t{\n' yield '\t\tif(beats < gFloatData[shotTimeCursor * 2 + %s])\n\t\t{\n' % ( shotTimesStart + 1) yield '\t\t\tlocalBeats = beats - gFloatData[shotTimeCursor * 2 + %s];\n' % shotTimesStart yield '\t\t\treturn shotTimeCursor;\n' yield '\t\t}\n' yield '\t}\n\twhile(++shotTimeCursor < %s);\n' % len(shots) yield '\treturn -1;\n' yield '}\n' global gShotScene gShotScene = ints.addInts([shot[2] for shot in shots]) flatAnimationData = [] animationDataPtrs = [] for shot in shots: animationDataPtrs += [len(flatAnimationData), len(shot[3].keys())] global gAnimEntriesMax gAnimEntriesMax = max(gAnimEntriesMax, len(shot[3].keys())) for uniformStringId in shot[3]: animationData = shot[3][uniformStringId] flatAnimationData += [uniformStringId, len(animationData)] for pair in animationData: flatAnimationData += pair flatAnimationData += [0] * (2 * (4 - len(animationData))) global gShotAnimationDataIds gShotAnimationDataIds = ints.addInts(animationDataPtrs) global gShotUniformData gShotUniformData = ints.addInts(flatAnimationData) def _serializeAll(scenes, shots): buffer = list(_serializeShots(shots)) for serializable in (text, floats): for ln in serializable.serialize(): yield ln buffer2 = [] for serializable in (shaders, framebuffers, passes): buffer2 += list(serializable.serialize()) global gScenePassIds gScenePassIds = ints.addInts(scenes) for ln in ints.serialize(): yield ln for ln in buffer2: yield ln for ln in buffer: yield ln data = [''.join(_serializeAll(scenes, shots))] data.append( """\n\n__forceinline float evalCurve(const float* data, int numFloats, float beats) { \tif(numFloats == 4 || beats <= data[1]) // 1 key or evaluating before first frame \t\treturn data[2]; \t// Find index of first key that has a bigger time than our current time \t// if none, this will be the index of the last key. \tint keyValueCount = numFloats; \tint rightKeyIndex = 4; \twhile (rightKeyIndex < keyValueCount - 4 && data[rightKeyIndex + 1] < beats) \t\trightKeyIndex += 4; \t// Clamp our sampling time to our range \tfloat sampleTime = (beats > data[rightKeyIndex + 1]) ? data[rightKeyIndex + 1] : beats; \t// Retrieve our spline points \tfloat y0 = data[rightKeyIndex - 2]; \tfloat y1 = data[rightKeyIndex - 1]; \t// handle stepped tangents \tif(y1 == FLT_MAX) return y0; \tfloat y2 = data[rightKeyIndex]; \tfloat y3 = data[rightKeyIndex + 2]; \tfloat dy = y3 - y0; \tfloat c0 = y1 + y2 - dy - dy; \tfloat c1 = dy + dy + dy - y1 - y1 - y2; \tfloat c2 = y1; \tfloat c3 = y0; \t// Determine factor \tfloat dt = data[rightKeyIndex + 1] - data[rightKeyIndex - 3]; \tfloat t = (sampleTime - data[rightKeyIndex - 3]) / dt; \treturn t * (t * (t * c0 + c1) + c2) + c3; } #define gAnimEntriesMax %s #define gShotAnimationDataIds %s #define gShotScene %s #define gScenePassIds %s #define gPassProgramsAndTargets %s #define gShotUniformData %s #define gFrameBufferData %s #define gFrameBufferBlockSize %s #define gProgramCount %s """ % (gAnimEntriesMax, gShotAnimationDataIds, gShotScene, gScenePassIds, gPassProgramsAndTargets, gShotUniformData, gFrameBufferData, FrameBufferPool.BLOCK_SIZE, len(shaders.offsets))) dst = FilePath(__file__).abs().parent().parent().join( 'Player', 'generated.hpp') with dst.edit() as fh: fh.write(''.join(data))
def currentProjectFilePath(): if not gSettings.contains('currentproject'): return None return FilePath(gSettings.value('currentproject'))
def _rebuild(self, path, index=None): if path: path = FilePath(path) time.sleep(0.01) if not path.exists(): # the scene has been deleted, stop watching it return self.fileSystemWatcher.addPath(path) path = path.abs() for i, passData in enumerate(self.passes): vert = True frag = True # make sure the changed path is in our dependencies if path: if not path in (stitch.abs() for stitch in passData.vertStitches): vert = False if not path in (stitch.abs() for stitch in passData.fragStitches): frag = False if not vert and not frag: continue if index is not None and index != i: continue includePaths = set() errors = [] vertCode = [] for stitch in passData.vertStitches: try: vertCode.append(_loadGLSLWithIncludes( stitch, includePaths)) except IOError as e: errors.append(stitch.abs()) fragCode = [] for stitch in passData.fragStitches: try: fragCode.append(_loadGLSLWithIncludes( stitch, includePaths)) except IOError as e: errors.append(stitch.abs()) if errors: QMessageBox.critical( None, 'Missing files', 'A template or scene could not be loaded & is missing the following files:\n\n%s' % '\n'.join(errors)) return if includePaths: self.fileSystemWatcher.addPaths(list(includePaths)) # not joining causes "unexpected $undefined" errors during shader compilation, # no idea why it injects invalid bytes if not vertCode: vertCode = Scene.STATIC_VERT else: vertCode = '\n'.join(vertCode) fragCode = '\n'.join(fragCode) try: program = gShaderPool.compileProgram(vertCode, fragCode) except RuntimeError as e: self.shaders = [] errors = e.args[0].split('\n') try: code = e.args[1][0].decode('ascii').split('\n') except IndexError: print(e.args) print('pass: '******'fragCode:') print(fragCode) return # html escape output errors = [Qt.escape(ln) for ln in errors] code = [Qt.escape(ln) for ln in code] log = [] for error in errors: try: lineNumber = int( error.split(' : ', 1)[0].rsplit('(')[-1].split(')')[0]) except: continue lineNumber -= 1 log.append( '<p><font color="red">%s</font><br/>%s<br/><font color="#081">%s</font><br/>%s</p>' % (error, '<br/>'.join( code[lineNumber - 5:lineNumber]), code[lineNumber], '<br/>'.join(code[lineNumber + 1:lineNumber + 5]))) self.__errorDialogText.setHtml('<pre>' + '\n'.join(log) + '</pre>') self.__errorDialog.setGeometry(100, 100, 800, 600) self.__errorDialog.exec_() return while len(self.shaders) <= i: self.shaders.append(0) self.shaders[i] = program # 3D texture dirties, let's reset it's buffers too if self.passes[i].is3d and self.colorBuffers: for j, buffer in enumerate(self.colorBuffers[i]): if isinstance(buffer, Texture3D): self.colorBuffers[i][j] = buffer.original self.__passDirtyState[i] = True self.__passDirtyState = [True] * len(self.passes) self.__errorDialog.close()
def __openProject(self, path): setCurrentProjectFilePath(FilePath(path)) self.__sceneList.projectOpened() self.__shotsManager.projectOpened() self._timer.projectOpened()
def _deserializePasses(sceneFile): """ :type sceneFile: FilePath :rtype: list[PassData] """ assert isinstance(sceneFile, FilePath) sceneDir = sceneFile.stripExt() templatePath = templatePathFromScenePath(sceneFile) templateDir = templatePath.stripExt() xTemplate = parseXMLWithIncludes(templatePath) passes = [] frameBufferMap = {} for xPass in xTemplate: buffer = -1 if 'buffer' in xPass.attrib: buffer = xPass.attrib['buffer'] if buffer not in frameBufferMap: frameBufferMap[buffer] = len(frameBufferMap) size = None if 'size' in xPass.attrib: size = int(xPass.attrib['size']), int(xPass.attrib['size']) elif 'width' in xPass.attrib and 'height' in xPass.attrib: size = int(xPass.attrib['width']), int(xPass.attrib['height']) tile = size is not None if 'tile' in xPass.attrib: tile = xPass.attrib['tile'].lower() == 'true' factor = None if 'factor' in xPass.attrib: factor = int(xPass.attrib['factor']) realtime = int(xPass.attrib.get('static', 0)) == 0 is3d = int(xPass.attrib.get('is3d', 0)) != 0 if is3d: assert (size[0]**0.5) == size[1] size = size[0], size[1] outputs = int(xPass.attrib.get('outputs', 1)) inputs = [] i = 0 key = 'input%s' % i while key in xPass.attrib: # input is filename? parentPath = currentProjectDirectory() fullName = parentPath.join(xPass.attrib[key]) if fullName.exists(): inputs.append(FilePath(xPass.attrib[key])) else: # input is buffer if '.' in xPass.attrib[key]: frameBuffer, subTexture = xPass.attrib[key].split('.') frameBuffer, subTexture = frameBuffer, int(subTexture) else: frameBuffer, subTexture = xPass.attrib[key], 0 if frameBuffer not in frameBufferMap: frameBufferMap[frameBuffer] = len(frameBufferMap) inputs.append((frameBufferMap[frameBuffer], subTexture)) i += 1 key = 'input%s' % i vertStitches = [] fragStitches = [] uniforms = {} for xElement in xPass: path = FilePath(xElement.attrib['path']) stitches = vertStitches if path.hasExt('vert') else fragStitches if xElement.tag.lower() == 'section': stitches.append(sceneDir.join(path)) elif xElement.tag.lower() in ('shared', 'global'): stitches.append(templateDir.join(path)) else: raise ValueError('Unknown XML tag in pass: "******"' % xElement.tag) for xUniform in xElement: uniforms[xUniform.attrib['name']] = [ float(x.strip()) for x in xUniform.attrib['value'].split(',') ] passes.append( PassData(vertStitches, fragStitches, uniforms, inputs, frameBufferMap.get(buffer, -1), realtime, size, tile, factor, outputs, xPass.attrib.get('drawcommand', None), is3d, xPass.attrib.get('name', None))) return passes
def _deserializePasses(sceneFile, models): """ :type sceneFile: FilePath :rtype: list[PassData] """ assert isinstance(sceneFile, FilePath) sceneDir = sceneFile.stripExt() templatePath = templatePathFromScenePath(sceneFile) templateDir = templatePath.stripExt() modelsDir = currentModelsDirectory() xTemplate = parseXMLWithIncludes(templatePath) passes = [] frameBufferMap = {} # # Start with adding the models here as passes. Stored by their model name # for model in models.models: # inputs = [] # if model.name not in frameBufferMap: # frameBufferMap[model.name] = len(frameBufferMap) # size = 256,256 # fragStitches = [] # fragStitches.append(templateDir.join("header.glsl")) # fragStitches.append(templateDir.join("noiselib.glsl")) # fragStitches.append(templateDir.join("sdf.glsl")) # #fragStitches.append(templateDir.join("test3d.glsl")) # fragStitches.append(modelsDir.join("%s.glsl" % model.name)) # # # Add a pass for rendering a 3D texture # passes.append( # PassData([], fragStitches, {}, inputs, frameBufferMap.get(model.name, -1), False, size, False, False, 1, None, True, None)) for xPass in xTemplate: buffer = -1 if 'buffer' in xPass.attrib: buffer = xPass.attrib['buffer'] if buffer not in frameBufferMap: frameBufferMap[buffer] = len(frameBufferMap) size = None if 'size' in xPass.attrib: size = int(xPass.attrib['size']), int(xPass.attrib['size']) elif 'width' in xPass.attrib and 'height' in xPass.attrib: size = int(xPass.attrib['width']), int(xPass.attrib['height']) tile = size is not None if 'tile' in xPass.attrib: tile = xPass.attrib['tile'].lower() == 'true' factor = None if 'factor' in xPass.attrib: factor = int(xPass.attrib['factor']) realtime = int(xPass.attrib.get('static', 0)) == 0 is3d = int(xPass.attrib.get('is3d', 0)) != 0 if is3d: assert (size[0] == size[1]) size = size[0], size[0] outputs = int(xPass.attrib.get('outputs', 1)) inputs = [] inputsUniformOverrideNames = {} i = 0 key = 'input%s' % i while key in xPass.attrib: # input is filename? parentPath = currentProjectDirectory() fullName = parentPath.join(xPass.attrib[key]) if fullName.exists(): inputs.append(FilePath(xPass.attrib[key])) else: # input is buffer if '.' in xPass.attrib[key]: frameBuffer, subTexture = xPass.attrib[key].split('.') frameBuffer, subTexture = frameBuffer, int(subTexture) else: frameBuffer, subTexture = xPass.attrib[key], 0 if frameBuffer not in frameBufferMap: frameBufferMap[frameBuffer] = len(frameBufferMap) inputs.append((frameBufferMap[frameBuffer], subTexture)) i += 1 key = 'input%s' % i inputModels = False if 'inputModels' in xPass.attrib: inputModels = xPass.attrib['inputModels'].lower() == 'true' # Add all model's output buffers as inputs to this pass if inputModels: for model in models.models: bufferIndex = (frameBufferMap[model.name], 0) inputs.append(bufferIndex) inputsUniformOverrideNames[ bufferIndex] = "uModel%s" % model.name vertStitches = [] fragStitches = [] uniforms = {} for xElement in xPass: if xElement.tag.lower() == 'section': path = FilePath(xElement.attrib['path']) stitches = vertStitches if path.hasExt( 'vert') else fragStitches stitches.append(sceneDir.join(path)) elif xElement.tag.lower() in ('shared', 'global'): path = FilePath(xElement.attrib['path']) stitches = vertStitches if path.hasExt( 'vert') else fragStitches stitches.append(templateDir.join(path)) elif xElement.tag.lower() == 'models': for model in models.models: fragStitches.append(modelsDir.join("%s.glsl" % model.name)) else: raise ValueError('Unknown XML tag in pass: "******"' % xElement.tag) for xUniform in xElement: uniforms[xUniform.attrib['name']] = [ float(x.strip()) for x in xUniform.attrib['value'].split(',') ] passes.append( PassData(vertStitches, fragStitches, uniforms, inputs, frameBufferMap.get(buffer, -1), realtime, size, tile, factor, outputs, xPass.attrib.get('drawcommand', None), is3d, xPass.attrib.get('name', None), inputsUniformOverrideNames)) return passes