##################################################################### # -*- coding: iso-8859-1 -*- # # # # Frets on Fire # # Copyright (C) 2009 Vlad Emelyanov # # # # This program is free software; you can redistribute it and/or # # modify it under the terms of the GNU General Public License # # as published by the Free Software Foundation; either version 2 # # of the License, or (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program; if not, write to the Free Software # # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # # MA 02110-1301, USA. # ##################################################################### from OpenGL.GL import * import os import sys import string from random import random import time import Log import pygame.image import Config import Version #OGL constants for compatibility with all PyOpenGL versions #now multitexturing should work even in PyOpenGL 2.x, if your card supports ARB ext #stump: managed to eliminate the need for every one of these. If I was wrong, please uncomment only the necessary line and notify me. #GL_TEXTURE_3D = 32879 #GL_TEXTURE_WRAP_R = 32882 #GL_TEXTURE0_ARB, GL_TEXTURE1_ARB, GL_TEXTURE2_ARB, GL_TEXTURE3_ARB = 33984, 33985, 33986, 33987 #GL_FRAGMENT_SHADER_ARB = 0x8B30 #GL_VERTEX_SHADER_ARB = 0x8B31 #GL_OBJECT_COMPILE_STATUS_ARB= 0x8B81 #GL_OBJECT_LINK_STATUS_ARB = 0x8B82 #GL_INFO_LOG_LENGTH_ARB = 0x8B84 #GL_CLAMP_TO_EDGE = 33071 # stump: these don't throw the exception for being unsupported - that happens later. # There has to be an active OpenGL context already for the checking to occur. # We do the check down in ShaderList.set(). # evilynux : Wrapped around try/except as pyopengl < 2.0.2.x fails here. try: from OpenGL.GL.ARB.shader_objects import * from OpenGL.GL.ARB.vertex_shader import * from OpenGL.GL.ARB.fragment_shader import * from OpenGL.GL.ARB.multitexture import * from OpenGL.GL.EXT.texture3D import * except: Log.warn("Importing OpenGL ARB fails therefore shaders are disabled."\ " It is most likely that your version of PyOpenGL is too old.") glInitShaderObjectsARB = lambda: False class ShaderCompilationError(Exception): pass #stump: apply some fixups for pyOpenGL 2.x if necessary. def checkFunctionsForPyOpenGL2x(): global glShaderSourceARB global glGetObjectParameterivARB # Check the version of pyOpenGL. import OpenGL if OpenGL.__version__ < '3': #stump: the binding of glShaderSourceARB() in pyOpenGL 2.x segfaults on use... ugh! # (It and glGetObjectParameterivARB() also have incompatible declarations - let's fix that too.) try: from ctypes import c_int, c_char_p, POINTER, cdll, byref if os.name == 'nt': from ctypes import WINFUNCTYPE, windll elif sys.platform == 'darwin': from ctypes.util import find_library except ImportError: raise ShaderCompilationError, 'ctypes is required to use shaders with pyOpenGL 2.x.' else: if os.name == 'nt': # Windows - look for the functions using wglGetProcAddress() # Grab the function pointers the standard Windows way. # (opengl32.dll doesn't directly export extension entry points like other platforms' OpenGL libraries do.) ptr_glShaderSourceARB = windll.opengl32.wglGetProcAddress('glShaderSourceARB') ptr_glGetObjectParameterivARB = windll.opengl32.wglGetProcAddress('glGetObjectParameterivARB') # If we got good function pointers, make them callable using ctypes. if ptr_glShaderSourceARB: func_glShaderSourceARB = WINFUNCTYPE(None, c_int, c_int, POINTER(c_char_p), POINTER(c_int))(ptr_glShaderSourceARB) else: raise ShaderCompilationError, 'wglGetProcAddress("glShaderSourceARB") returned NULL - are shaders supported?' if ptr_glGetObjectParameterivARB: func_glGetObjectParameterivARB = WINFUNCTYPE(None, c_int, c_int, POINTER(c_int))(ptr_glGetObjectParameterivARB) else: raise ShaderCompilationError, 'wglGetProcAddress("glGetObjectParameterivARB") returned NULL - are shaders supported?' else: # something else - assume that the OpenGL library exports the extension entry point # Figure out where the OpenGL library is. if sys.platform == 'darwin': # Mac OS X glLibrary = cdll.LoadLibrary(find_library('OpenGL')) else: # something else; most likely GNU/Linux glLibrary = cdll.LoadLibrary('libGL.so') try: func_glShaderSourceARB = glLibrary.glShaderSourceARB func_glGetObjectParameterivARB = glLibrary.glGetObjectParameterivARB except: raise ShaderCompilationError, 'Cannot find glShaderSourceARB() and/or glGetObjectParameterivARB() in the OpenGL library - are shaders supported?' # Wrap supporting glShaderSource(shader object, iterable object) returning None, as used below. def glShaderSourceARB(shader, source): srcList = list(source) srcListType = c_char_p * len(srcList) func_glShaderSourceARB(shader, len(srcList), srcListType(*srcList), None) # Wrap supporting glGetObjectParameterivARB(shader object, parameter id) returning int, as used below. def glGetObjectParameterivARB(shader, param): retval = c_int(0) func_glGetObjectParameterivARB(shader, param, byref(retval)) return retval.value #should be placed somewhere else if sys.platform == 'darwin': global GL_TEXTURE_3D_EXT global GL_TEXTURE_WRAP_R_EXT global glTexImage3DEXT GL_TEXTURE_3D_EXT = GL_TEXTURE_3D GL_TEXTURE_WRAP_R_EXT = GL_TEXTURE_WRAP_R glTexImage3DEXT = glTexImage3D # main class for shaders library class ShaderList: def __init__(self): self.shaders = {} # list of all shaders self.active = 0 # active shader self.texcount = 0 self.workdir = "" # dir that contains shader files self.enabled = False # true if shaders are compiled self.turnon = False # true if shaders are enabled in settings self.var = {} # different variables self.assigned = {} # list for shader replacement self.globals = {} # list of global vars for every shader def make(self, fname, name = ""): """Compile a shader. fname = base filename for shader files name = name to use for this shader (defaults to fname) Returns nothing, or raises an exception on error.""" if name == "": name = fname fullname = os.path.join(self.workdir, fname) vertname, fragname = fullname+".vert", fullname+".frag" Log.debug('Compiling shader "%s" from %s and %s.' % (name, vertname, fragname)) program = self.compile(open(vertname), open(fragname)) sArray = {"program": program, "name": name, "textures": []} self.getVars(vertname, program, sArray) self.getVars(fragname, program, sArray) self.shaders[name] = sArray if self.shaders[name].has_key("Noise3D"): self.setTexture("Noise3D",self.noise3D,name) def compileShader(self, source, shaderType): """Compile shader source of given type. source = file object open to shader source code (or something else returning lines of GLSL code when iterated over) shaderType = GL_VERTEX_SHADER_ARB or GL_FRAGMENT_SHADER_ARB Returns the shader object, or raises an exception on error.""" shader = glCreateShaderObjectARB(shaderType) glShaderSourceARB( shader, source ) glCompileShaderARB( shader ) status = glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB) if not status: raise ShaderCompilationError, self.log(shader) else: return shader def compile(self, vertexSource, fragmentSource): """Create an OpenGL program object from file objects open to the source files. vertexSource = file object open to vertex shader source code fragmentSource = file object open to fragment shader source code Returns the program object, or raises an exception on error.""" program = glCreateProgramObjectARB() vertexShader = self.compileShader(vertexSource, GL_VERTEX_SHADER_ARB) fragmentShader = self.compileShader(fragmentSource, GL_FRAGMENT_SHADER_ARB) glAttachObjectARB(program, vertexShader) glAttachObjectARB(program, fragmentShader) glValidateProgramARB( program ) glLinkProgramARB(program) glDeleteObjectARB(vertexShader) glDeleteObjectARB(fragmentShader) return program def getVars(self, fname, program, sArray): """Read the names of uniform variables from a shader file. fname = shader filename program = OpenGL program object compiled from that shader file sArray = dictionary to add variable information to""" for line in open(fname): aline = line[:string.find(line,";")] aline = aline.split(' ') if '(' in aline[0]: break if aline[0] == "uniform": value = None try: n = int(aline[1][-1]) except: n = 4 if aline[1] == "bool": value = False elif aline[1] == "int": value = 0 elif aline[1] == "float": value = 0.0 elif aline[1][:-1] == "bvec": value = (False,)*n elif aline[1][:-1] == "ivec": value = (0,)*n elif aline[1][:-1] == "vec": value = (.0,)*n elif aline[1][:-1] == "mat": value = ((.0,)*n,)*n elif aline[1][:-2] == "sampler": value, self.texcount = self.texcount, self.texcount + 1 if aline[1] == "sampler1D": textype = GL_TEXTURE_1D elif aline[1] == "sampler2D": textype = GL_TEXTURE_2D elif aline[1] == "sampler3D": textype = GL_TEXTURE_3D_EXT sArray["textures"].append((aline[2],textype,0)) aline[2] = aline[2].split(',') for var in aline[2]: sArray[var] = [glGetUniformLocationARB(program, var), value] #simplified texture binding function def setTexture(self,name,texture,program = None): if self.assigned.has_key(program): program = self.assigned[program] if program == None: program = self.active else: program = self[program] for i in range(len(program["textures"])): if program["textures"][i][0] == name: program["textures"][i] = (program["textures"][i][0], program["textures"][i][1], texture) return True return False def getVar(self, var = "program", program = None): """Get a uniform variable value from a shader. var = variable name program = shader name, or None (default) for the currently active shader Returns the value. If the variable does not exist, KeyError is raised.""" if self.assigned.has_key(program): program = self.assigned[program] if program is None: program = self.active else: program = self[program] if program is None or not program.has_key(var): return False else: return program[var][1] return True def setVar(self, var, value, program = None): """Set a uniform variable value for a shader. var = variable name value = new value program = shader name, or None (default) for the currently active shader Returns nothing. If the variable does not exist, KeyError is raised.""" if self.assigned.has_key(program): program = self.assigned[program] if program is None: program = self.active else: program = self[program] if program is None or not program.has_key(var): return if type(value) == str: if self.var.has_key(value): value = self.var[value] else: return pos = program[var] pos[1] = value if program == self.active: if type(value) == list: value = tuple(value) if type(value) == bool: if pos[1]: glUniform1iARB(pos[0],1) else: glUniform1iARB(pos[0],0) elif type(value) == float: glUniform1fARB(pos[0],pos[1]) elif type(value) == int: glUniform1iARB(pos[0],pos[1]) elif type(value) == tuple: if type(value[0]) == float: if len(value) == 2: glUniform2fARB(pos[0],*pos[1]) elif len(value) == 3: glUniform3fARB(pos[0],*pos[1]) elif len(value) == 4: glUniform4fARB(pos[0],*pos[1]) elif type(value[0]) == int: if len(value) == 2: glUniform2iARB(pos[0],*pos[1]) elif len(value) == 3: glUniform3iARB(pos[0],*pos[1]) elif len(value) == 4: glUniform4iARB(pos[0],*pos[1]) elif type(value) == long: glUniform1iARB(pos[0],pos[1]) else: raise TypeError, 'Unsupported value type (must be bool, float, int, long, or tuple or list of float or int).' # slightly changes uniform variable def modVar(self, var, value, effect = 0.05, alphaAmp=1.0, program = None): old = self.getVar(var,program) if old is None: return False if type(old) == tuple: new = () for i in range(len(old)): if i==3: new += (old[i] * (1-effect) + value[i] * effect*alphaAmp,) else: new += (old[i] * (1-effect) + value[i] * effect,) else: new = old * (1-effect) + value * effect self.setVar(var,new,program) return True # enables shader program def enable(self, shader): if not self.turnon: return False if self.assigned.has_key(shader): shader = self.assigned[shader] if self[shader] is None: return False glUseProgramObjectARB(self[shader]["program"]) self.active = self.shaders[shader] self.setTextures() self.update() self.globals["time"] = self.time() self.setGlobals() if self.getVar("time"): self.setVar("dt",self.globals["time"]-self.getVar("time")) return True # transmit global vars to uniforms def setGlobals(self): for i in self.globals.keys(): self.setVar(i,self.globals[i]) # update all uniforms def update(self): for i in self.active.keys(): if i != "textures": if type(self.active[i]) == list and self.active[i][1] is not None: self.setVar(i,self.active[i][1]) # set standart OpenGL program active def disable(self): if self.active != 0: glUseProgramObjectARB(0) self.active = 0 # return active program control def activeProgram(self): if self.active != 0: return self.active["name"] else: return 0 def log(self, shader): """Get the error log for a shader. shader = object to get log from Returns a string containing the log or None if there is no log.""" length = glGetObjectParameterivARB(shader, GL_INFO_LOG_LENGTH) if length > 0: log = glGetInfoLogARB(shader) return log # update and bind all textures def setTextures(self, program = None): if self.assigned.has_key(program): program = self.assigned[program] if program is None: program = self.active for i in range(len(program["textures"])): glActiveTextureARB(self.multiTex[i]) glBindTexture(program["textures"][i][1], program["textures"][i][2]) def makeNoise3D(self,size=32, c = 1, type = GL_RED): texels=[] for i in range(size): arr2 = [] for j in range(size): arr = [] for k in range(size): arr.append(random()) arr2.append(arr) texels.append(arr2) self.smoothNoise3D(size, 2, texels) for i in range(size): for j in range(size): for k in range(size): texels[i][j][k] = int(255 * texels[i][j][k]) texture = 0 glBindTexture(GL_TEXTURE_3D_EXT, texture) glTexParameterf(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_R_EXT, GL_REPEAT) glTexParameterf(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameterf(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_T, GL_REPEAT) glTexParameterf(GL_TEXTURE_3D_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_3D_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexImage3DEXT(GL_TEXTURE_3D_EXT, 0, c,size, size, size, 0, type, GL_UNSIGNED_BYTE, texels) return texture def makeNoise2D(self,size=64, c = 1, type = GL_RED): texels=[] for i in range(size): texels.append([]) for j in range(size): texels[i].append(random()) self.smoothNoise(size, 2, texels) self.smoothNoise(size, 3, texels) self.smoothNoise(size, 4, texels) for i in range(size): for j in range(size): texels[i][j] = int(255 * texels[i][j]) texture = 0 glBindTexture(GL_TEXTURE_2D, texture) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexImage2D(GL_TEXTURE_2D, 0, c,size, size, 0, type, GL_UNSIGNED_BYTE, texels) return texture def loadTex3D(self, fname, type = GL_RED): file = os.path.join(self.workdir,fname) if os.path.exists(file): noise = open(file).read() size = int(len(noise)**(1/3.0)) else: Log.debug("Can't load %s; generating random 3D noise instead." % file) return self.makeNoise3D(16) #self.smoothNoise3D(size, 2, texels) #self.smoothNoise3D(size, 4, texels) texture = 0 glBindTexture(GL_TEXTURE_3D_EXT, texture) glTexParameterf(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameterf(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_T, GL_REPEAT) glTexParameterf(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_R_EXT, GL_REPEAT) glTexParameterf(GL_TEXTURE_3D_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_3D_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexImage3DEXT(GL_TEXTURE_3D_EXT, 0, 1,size, size, size, 0, type, GL_UNSIGNED_BYTE, noise) return texture def loadTex2D(self, fname, type = GL_RGB): file = os.path.join(self.workdir,fname) if os.path.exists(file): img = pygame.image.load(file) noise = pygame.image.tostring(img, "RGB") else: Log.debug("Can't load %s; generating random 2D noise instead." % fname) return self.makeNoise2D(16) texture = 0 glBindTexture(GL_TEXTURE_2D, texture) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexImage2D(GL_TEXTURE_2D, 0, 1, img.get_width(), img.get_height(), 0, type, GL_UNSIGNED_BYTE, noise) return texture def smoothNoise(self, size, c, noise): for x in range(size): for y in range(size): col1 = noise[x][y] col2 = noise[size/2/(1-c)+x/c][size/2/(1-c)+y/c] noise[x][y] = (1-1/float(c))*col1+1/float(c)*col2 def smoothNoise3D(self, size, c, noise): for i in range(size): for j in range(size): for k in range(size): col1 = noise[i][j][k] col2 = noise[size/2/(1-c)+i/c][size/2/(1-c)+j/c][size/2/(1-c)+k/c] noise[i][j][k] = (1-1/float(c))*col1+1/float(c)*col2 def __getitem__(self, name): if self.shaders.has_key(name): return self.shaders[name] else: return None def time(self): return time.clock() def reset(self): self.checkIfEnabled() if self.turnon: self.var["color"] = {} #color for guitar neck flashing self.var["solocolor"] = (0.0,)*4 #color for GH3 solo lightnings self.var["eqcolor"] = (0.0,)*4 #color for equalizer self.var["fret"] = {} #last note hit time for each player self.var["fretpos"] = {} #last note hit pos for each player self.var["scoreMult"] = {} #score multiplier for each player self.var["multChangePos"] = {} #score multiplier last changing pos for each player self.globals["bpm"] = 120.0 self.globals["breActive"] = False self.globals["dfActive"] = False self.globals["isDrum"] = False self.globals["isFailing"] = False self.globals["isMultChanged"] = False self.globals["killswitch"] = False self.globals["killswitchPos"] = -10.0 self.globals["multChangePos"] = -10.0 self.globals["notepos"] = -10.0 self.globals["rockLevel"] = 0.5 self.globals["scoreMult"] = 1 self.globals["soloActive"] = False self.globals["songpos"] = 0.0 #self.loadFromIni() # check Settings to enable, disable or assign shaders def checkIfEnabled(self): if Config.get("video","shader_use"): if self.enabled: self.turnon = True else: self.set(os.path.join(Version.dataPath(), "shaders")) else: self.turnon = False if self.turnon: for i in self.shaders.keys(): value = Config.get("video","shader_"+i) if value != "None": if value == "theme": if Config.get("theme","shader_"+i) == "True": value = i else: continue self.assigned[i] = value return True return False def defineConfig(self): for name in self.shaders.keys(): for key in self[name].keys(): Config.define("shader", name+"_"+key, str, "None") def loadFromIni(self): for name in self.shaders.keys(): for key in self[name].keys(): value = Config.get("theme",name+"_"+key) if value != "None": if value == "True": value = True elif value == "False": value = False else: value = value.split(",") for i in range(len(value)): value[i] = float(value[i]) if len(value) == 1: value = value[0] else: value = tuple(value) if key == "enabled": if Config.get("video","shader_"+name) == 2: self[name][key] = value else: if len(self[name][key]) == 2: self[name][key][1] = value def set(self, dir): """Do all shader setup. dir = directory to load shaders from """ #stump: check whether all needed extensions are actually supported if not glInitShaderObjectsARB(): Log.warn('OpenGL extension ARB_shader_objects not supported - shaders disabled') return if not glInitVertexShaderARB(): Log.warn('OpenGL extension ARB_vertex_shader not supported - shaders disabled') return if not glInitFragmentShaderARB(): Log.warn('OpenGL extension ARB_fragment_shader not supported - shaders disabled') return if not glInitMultitextureARB(): Log.warn('OpenGL extension ARB_multitexture not supported - shaders disabled') return if not glInitTexture3DEXT(): if sys.platform != 'darwin': Log.warn('OpenGL extension EXT_texture3D not supported - shaders disabled') return #stump: pyOpenGL 2.x compatibility try: checkFunctionsForPyOpenGL2x() except: Log.error('Shaders disabled due to pyOpenGL 2.x compatibility issue: ') return self.workdir = dir # Load textures needed by the shaders. try: self.noise3D = self.loadTex3D("noise3d.dds") self.outline = self.loadTex2D("outline.tga") except: Log.error('Could not load shader textures - shaders disabled: ') return self.multiTex = (GL_TEXTURE0_ARB,GL_TEXTURE1_ARB,GL_TEXTURE2_ARB,GL_TEXTURE3_ARB) self.enabled = True self.turnon = True # Compile the shader objects that we are going to use. # Also set uniform shader variables to default values. try: self.make("lightning","stage") except: Log.error("Error compiling lightning shader: ") else: self.enable("stage") self.setVar("ambientGlowHeightScale",6.0) self.setVar("color",(0.0,0.0,0.0,0.0)) self.setVar("glowFallOff",0.024) self.setVar("height",0.44) self.setVar("sampleDist",0.0076) self.setVar("speed",1.86) self.setVar("vertNoise",0.78) self.setVar("fading",1.0) self.setVar("solofx",False) self.setVar("scalexy",(5.0,2.4)) self.setVar("fixalpha",True) self.setVar("offset",(0.0,-2.5)) self.disable() try: self.make("lightning","sololight") except: Log.error("Error compiling lightning shader: ") else: self.enable("sololight") self.setVar("scalexy",(5.0,1.0)) self.setVar("ambientGlow",0.5) self.setVar("ambientGlowHeightScale",6.0) self.setVar("solofx",True) self.setVar("height",0.3) self.setVar("glowFallOff",0.024) self.setVar("sampleDist",0.0076) self.setVar("fading",4.0) self.setVar("speed",1.86) self.setVar("vertNoise",0.78) self.setVar("solofx",True) self.setVar("color",(0.0,0.0,0.0,0.0)) self.setVar("fixalpha",True) self.setVar("glowStrength",100.0) self.disable() try: self.make("lightning","tail") except: Log.error("Error compiling lightning shader: ") else: self.enable("tail") self.setVar("scalexy",(5.0,1.0)) self.setVar("ambientGlow",0.1) self.setVar("ambientGlowHeightScale",6.0) self.setVar("solofx",True) self.setVar("fading",4.0) self.setVar("height",0.0) self.setVar("glowFallOff",0.024) self.setVar("sampleDist",0.0076) self.setVar("speed",1.86) self.setVar("vertNoise",0.78) self.setVar("solofx",True) self.setVar("color",(0.3,0.7,0.9,0.6)) self.setVar("glowStrength",70.0) self.setVar("fixalpha",True) self.setVar("offset",(0.0,0.0)) self.disable() try: self.make("rockbandtail","tail2") except: Log.error("Error compiling rockbandtail shader: ") else: self.enable("tail2") self.setVar("height",0.2) self.setVar("color",(0.0,0.6,1.0,1.0)) self.setVar("speed",9.0) self.setVar("offset",(0.0,0.0)) self.setVar("scalexy",(5.0,1.0)) self.disable() try: self.make("metal","notes") except: Log.error("Error compiling metal shader: ") else: self.enable("notes") self.disable() try: self.make("neck","neck") except: Log.error("Error compiling neck shader: ") try: self.make("cd","cd") except: Log.error("Error compiling cd shader: ") #self.defineConfig() def mixColors(c1,c2,blend=0.5): c1 = list(c1) c2 = list(c2) alpha = 0.0 for i in range(3): c1[i] = c1[i] + blend * c2[i] alpha += c1[i] c1 = c1[:3] + [min(alpha / 3.0,1.0)] return tuple(c1) shaders = ShaderList() del ShaderList