Created
February 19, 2016 14:52
-
-
Save DCubix/cd67a694d03420b561c9 to your computer and use it in GitHub Desktop.
Realtime GLSL Editor for BGE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
try: | |
from Tkinter import * | |
from ttk import * | |
import ScrolledText as scrolledtext | |
import tkMessageBox as msgbox | |
import tkFileDialog as filedlg | |
except ImportError: | |
from tkinter import * | |
from tkinter.ttk import * | |
from tkinter import scrolledtext | |
import tkinter.messagebox as msgbox | |
import tkinter.filedialog as filedlg | |
import re | |
import socket | |
import pickle | |
class HText(scrolledtext.ScrolledText): | |
'''A text widget with a new method, highlight_pattern() | |
example: | |
text = CustomText() | |
text.tag_configure("red", foreground="#ff0000") | |
text.highlight_pattern("this should be red", "red") | |
The highlight_pattern method is a simplified python | |
version of the tcl code at http://wiki.tcl.tk/3246 | |
''' | |
def __init__(self, *args, **kwargs): | |
scrolledtext.ScrolledText.__init__(self, *args, **kwargs) | |
def highlight_current(self, pattern, tag): | |
start = 1.0 | |
match = re.findall(pattern, self.get(start, END)) | |
for c in match: | |
pos = self.search(c, "insert linestart-1c", stopindex="insert lineend+1c") | |
while pos: | |
length = len(c) | |
row, col = pos.split('.') | |
end = int(col) + length | |
end = row + '.' + str(end) | |
self.tag_add(tag, pos, end) | |
start = end | |
pos = self.search(c, start, stopindex="insert lineend+1c") | |
def highlight_pattern(self, pattern, tag): | |
'''Apply the given tag to all text that matches the given pattern | |
If 'regexp' is set to True, pattern will be treated as a regular | |
expression according to Tcl's regular expression syntax. | |
''' | |
start = 1.0 | |
match = re.findall(pattern, self.get(start, END)) | |
for c in match: | |
pos = self.search(c, "1.0", stopindex=END) | |
while pos: | |
length = len(c) | |
row, col = pos.split('.') | |
end = int(col) + length | |
end = row + '.' + str(end) | |
self.tag_add(tag, pos, end) | |
start = end | |
pos = self.search(c, start, stopindex=END) | |
class App(Frame): | |
def __init__(self, parent): | |
Frame.__init__(self, parent) | |
self.parent = parent | |
self.modified = False | |
self.fileName = "" | |
self.kw1 = [ | |
"float","double","int","void","bool","true","false","mat2","mat3", | |
"mat4","dmat2","dmat3","dmat4","mat2x2","mat2x3","mat2x4","dmat2x2", | |
"dmat2x3","dmat2x4","mat3x2","mat3x3","mat3x4","dmat3x2","dmat3x3", | |
"dmat3x4","mat4x2","mat4x3","mat4x4","dmat4x2","dmat4x3","dmat4x4","vec2", | |
"vec3","vec4","ivec2","ivec3","ivec4","bvec2","bvec3","bvec4","dvec2", | |
"dvec3","dvec4","uint","uvec2","uvec3","uvec4","sampler1D","sampler2D", | |
"sampler3D","samplerCube","sampler1DShadow","sampler2DShadow", | |
"samplerCubeShadow","sampler1DArray","sampler2DArray", | |
"sampler1DArrayShadow","sampler2DArrayShadow","isampler1D","isampler2D", | |
"isampler3D","isamplerCube","isampler1DArray","isampler2DArray", | |
"usampler1D","usampler2D","usampler3D","usamplerCube","usampler1DArray", | |
"usampler2DArray","sampler2DRect","sampler2DRectShadow","isampler2DRect", | |
"usampler2DRect","samplerBuffer","isamplerBuffer","usamplerBuffer", | |
"sampler2DMS","isampler2DMS","usampler2DMS","sampler2DMSArray", | |
"isampler2DMSArray","usampler2DMSArray","samplerCubeArray", | |
"samplerCubeArrayShadow","isamplerCubeArray","usamplerCubeArray", | |
"image1D","iimage1D","uimage1D","image2D","iimage2D","uimage2D","image3D", | |
"iimage3D","uimage3D","image2DRect","iimage2DRect","uimage2DRect", | |
"imageCube","iimageCube","uimageCube","imageBuffer","iimageBuffer", | |
"uimageBuffer","image1DArray","iimage1DArray","uimage1DArray", | |
"image2DArray","iimage2DArray","uimage2DArray","imageCubeArray", | |
"iimageCubeArray","uimageCubeArray","image2DMS","iimage2DMS","uimage2DMS", | |
"image2DMSArray","iimage2DMSArray","uimage2DMSArray","long","short", | |
"half","fixed","unsigned","hvec2","hvec3","hvec4","fvec2","fvec3","fvec4" | |
"sampler3DRect", | |
"attribute","const","uniform","varying","buffer","shared","coherent","volatile","restrict", | |
"readonly","writeonly","atomic_uint","layout","centroid","flat","smooth", | |
"noperspective","patch","sample","break","continue","do","for","while", | |
"switch","case","default","if","else","subroutine","in","out","inout", | |
"invariant","discard","return","lowp","mediump","highp","precision", | |
"struct","common","partition","active","asm","class","union","enum", | |
"typedef","template","this","packed","resource","goto","inline","noinline", | |
"public","static","extern","external","interface","superp","input","output", | |
"filter","sizeof","cast","namespace","using","row_major", | |
"early_fragment_tests", "varying", "attribute" | |
] | |
self.kw3 = [ | |
"abs","acos","acosh","all","any","asin","asinh","atan","atanh", | |
"atomicCounter","atomicCounterDecrement","atomicCounterIncrement", | |
"barrier","bitCount","bitfieldExtract","bitfieldInsert","bitfieldReverse", | |
"ceil","clamp","cos","cosh","cross","degrees","determinant","dFdx","dFdy", | |
"dFdyFine","dFdxFine","dFdyCoarse","dFdxCourse", | |
"fwidthFine","fwidthCoarse", | |
"distance","dot","EmitStreamVertex","EmitVertex","EndPrimitive", | |
"EndStreamPrimitive","equal","exp","exp2","faceforward","findLSB", | |
"findMSB","floatBitsToInt","floatBitsToUint","floor","fma","fract", | |
"frexp","fwidth","greaterThan","greaterThanEqual","imageAtomicAdd", | |
"imageAtomicAnd","imageAtomicCompSwap","imageAtomicExchange", | |
"imageAtomicMax","imageAtomicMin","imageAtomicOr","imageAtomicXor", | |
"imageLoad","imageSize","imageStore","imulExtended","intBitsToFloat", | |
"imageSamples", | |
"interpolateAtCentroid","interpolateAtOffset","interpolateAtSample", | |
"inverse","inversesqrt","isinf","isnan","ldexp","length","lessThan", | |
"lessThanEqual","log","log2","matrixCompMult","max","memoryBarrier","min", | |
"mix","mod","modf","noise","normalize","not","notEqual","outerProduct", | |
"packDouble2x32","packHalf2x16","packSnorm2x16","packSnorm4x8", | |
"packUnorm2x16","packUnorm4x8","pow","radians","reflect","refract", | |
"round","roundEven","sign","sin","sinh","smoothstep","sqrt","step","tan", | |
"tanh","texelFetch","texelFetchOffset","texture","textureGather", | |
"textureGatherOffset","textureGatherOffsets","textureGrad", | |
"textureGradOffset","textureLod","textureLodOffset","textureOffset", | |
"textureProj","textureProjGrad","textureProjGradOffset","textureProjLod", | |
"textureProjLodOffset","textureProjOffset","textureQueryLevels","textureQueryLod", | |
"textureSize","transpose","trunc","uaddCarry","uintBitsToFloat", | |
"umulExtended","unpackDouble2x32","unpackHalf2x16","unpackSnorm2x16", | |
"unpackSnorm4x8","unpackUnorm2x16","unpackUnorm4x8","usubBorrow", | |
"texture1D", "texture1DProj", "texture1DLod", "texture1DProjLod", | |
"texture2D", "texture2DProj", "texture2DLod", "texture2DProjLod", | |
"texture2DRect", "texture2DRectProj", | |
"texture3D", "texture3DProj", "texture3DLod", "texture3DProjLod", | |
"shadow1D", "shadow1DProj", "shadow1DLod", "shadow1DProjLod", | |
"shadow2D", "shadow2DProj", "shadow2DLod", "shadow2DProjLod", | |
"textureCube", "textureCubeLod" | |
] | |
self.keywords = r"\b(?:%s)\b" % "|".join(self.kw1) | |
self.functions = r"\b(?:%s)\b" % "|".join(self.kw3) | |
self.builtin = r"gl_[^\s\)\(]*" | |
self.preprocessor = r"#.+" | |
self.comment = r"//.+" | |
self.keyword_color = "#CC1486" | |
self.function_color = "#CD2CF5" | |
self.builtin_color = "#2FD46E" | |
self.preprocessor_color = "#858585" | |
self.comment_color = "#858585" | |
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
self.sock.setblocking(False) | |
self.address = ("127.0.0.1", 8080) | |
initdata = { | |
"cmd": "GLE_START", | |
"data": None | |
} | |
self.sock.sendto(pickle.dumps(initdata), self.address) | |
self.__init_ui__() | |
def __init_ui__(self): | |
sty = Style() | |
sty.theme_use("clam") | |
self.parent.title("glEdit") | |
# WORKSPACE | |
note = Notebook(self.parent) | |
vshader = Frame(note) | |
fshader = Frame(note) | |
note.add(vshader, text="Vertex Shader") | |
note.add(fshader, text="Fragment Shader") | |
vshaderText = HText(vshader) | |
vshaderText.configure(bg="#121212", fg="#EBEBEB", insertbackground="#EBEBEB") | |
vshaderText.tag_configure("keywords", foreground=self.keyword_color) | |
vshaderText.tag_configure("functions", foreground=self.function_color) | |
vshaderText.tag_configure("builtin", foreground=self.builtin_color) | |
vshaderText.tag_configure("preprocessor", foreground=self.preprocessor_color) | |
vshaderText.tag_configure("comment", foreground=self.comment_color) | |
vshaderText.pack(fill=BOTH, expand=1) | |
def vshaderUpdate(e): | |
vshaderText.highlight_current(self.keywords, "keywords") | |
vshaderText.highlight_current(self.functions, "functions") | |
vshaderText.highlight_current(self.builtin, "builtin") | |
vshaderText.highlight_current(self.preprocessor, "preprocessor") | |
vshaderText.highlight_current(self.comment, "comment") | |
self.modified = True | |
vshaderText.bind("<KeyRelease>", vshaderUpdate) | |
vshaderText.bind("<Enter>", vshaderUpdate) | |
fshaderText = HText(fshader) | |
fshaderText.configure(bg="#121212", fg="#EBEBEB", insertbackground="#EBEBEB") | |
fshaderText.tag_configure("keywords", foreground=self.keyword_color) | |
fshaderText.tag_configure("functions", foreground=self.function_color) | |
fshaderText.tag_configure("builtin", foreground=self.builtin_color) | |
fshaderText.tag_configure("preprocessor", foreground=self.preprocessor_color) | |
fshaderText.tag_configure("comment", foreground=self.comment_color) | |
fshaderText.pack(fill=BOTH, expand=1) | |
def fshaderUpdate(e): | |
fshaderText.highlight_current(self.keywords, "keywords") | |
fshaderText.highlight_current(self.functions, "functions") | |
fshaderText.highlight_current(self.builtin, "builtin") | |
fshaderText.highlight_current(self.preprocessor, "preprocessor") | |
fshaderText.highlight_current(self.comment, "comment") | |
self.modified = True | |
fshaderText.bind("<KeyRelease>", fshaderUpdate) | |
fshaderText.bind("<Enter>", fshaderUpdate) | |
note.pack(side=TOP, fill=BOTH, expand=1) | |
# Main menu | |
mainMenu = Menu(self.parent) | |
self.parent.config(menu=mainMenu) | |
def NewProgramCMD(e=None): | |
if self.modified: | |
if msgbox.askyesno("New Program", "This shader program has not been saved. Do you want to proceed?"): | |
self.modified = False | |
vshaderText.delete("1.0", END) | |
fshaderText.delete("1.0", END) | |
else: | |
vshaderText.delete("1.0", END) | |
fshaderText.delete("1.0", END) | |
self.modified = False | |
def SaveFile(): | |
vert = vshaderText.get("1.0", END) | |
frag = fshaderText.get("1.0", END) | |
with open(self.fileName, "w") as f: | |
f.write("[Vertex Shader]\n") | |
f.write(frag.strip("\n ")+"\n") | |
f.write("[Fragment Shader]\n") | |
f.write(frag.strip("\n ")) | |
pdata = { | |
"cmd": "GLE_ALL_CHANGE", | |
"data": [vert.strip("\n"), frag.strip("\n")] | |
} | |
self.sock.sendto(pickle.dumps(pdata), self.address) | |
def SaveFileDLG(): | |
filename = filedlg.asksaveasfilename(filetypes=[("glEdit Shader Program", "*.glp")]) | |
if filename: | |
self.fileName = filename | |
SaveFile() | |
def LoadFile(): | |
filename = filedlg.askopenfilename(filetypes=[("glEdit Shader Program", "*.glp")]) | |
if filename: | |
self.fileName = filename | |
lines = [] | |
with open(filename, "r") as f: | |
lines = f.readlines() | |
vshaderText.delete("1.0", END) | |
fshaderText.delete("1.0", END) | |
vert = "" | |
frag = "" | |
mode = "" | |
for line in lines: | |
lowline = line.lower().strip() | |
if lowline == "[vertex shader]" or lowline == "[vs]" or lowline == "[vert]": | |
mode = "v" | |
continue | |
elif lowline == "[fragment shader]" or lowline == "[fs]" or lowline == "[frag]": | |
mode = "f" | |
continue | |
if mode == "v": | |
vert += line | |
elif mode == "f": | |
frag += line | |
vshaderText.insert(INSERT, vert) | |
fshaderText.insert(INSERT, frag) | |
vshaderText.highlight_pattern(self.keywords, "keywords") | |
vshaderText.highlight_pattern(self.functions, "functions") | |
vshaderText.highlight_pattern(self.builtin, "builtin") | |
vshaderText.highlight_pattern(self.preprocessor, "preprocessor") | |
vshaderText.highlight_pattern(self.comment, "preprocessor") | |
fshaderText.highlight_pattern(self.keywords, "keywords") | |
fshaderText.highlight_pattern(self.functions, "functions") | |
fshaderText.highlight_pattern(self.builtin, "builtin") | |
fshaderText.highlight_pattern(self.preprocessor, "preprocessor") | |
fshaderText.highlight_pattern(self.comment, "preprocessor") | |
pdata = { | |
"cmd": "GLE_ALL_CHANGE", | |
"data": [vert.strip("\n"), frag.strip("\n")] | |
} | |
self.sock.sendto(pickle.dumps(pdata), self.address) | |
def OpenProgramCMD(e=None): | |
if self.modified: | |
if msgbox.askyesno("Open Program", "This shader program has not been saved. Do you want to proceed?"): | |
self.modified = False | |
LoadFile() | |
else: | |
LoadFile() | |
self.modified = False | |
def SaveProgramCMD(e=None): | |
if self.modified: | |
if len(self.fileName) == 0: | |
SaveFileDLG() | |
else: | |
SaveFile() | |
self.modified = False | |
def SaveProgramAsCMD(e=None): | |
SaveFileDLG() | |
self.modified = False | |
def ExitCMD(e=None): | |
if self.modified: | |
if msgbox.askyesno("Exit", "The current program has not been saved. Are you sure?"): | |
pdata = { | |
"cmd": "GLE_QUIT", | |
"data": None | |
} | |
self.sock.sendto(pickle.dumps(pdata), self.address) | |
self.parent.quit() | |
self.sock.close() | |
else: | |
pdata = { | |
"cmd": "GLE_QUIT", | |
"data": None | |
} | |
self.sock.sendto(pickle.dumps(pdata), self.address) | |
seld.parent.quit() | |
self.sock.close() | |
self.parent.protocol("WM_DELETE_WINDOW", ExitCMD) | |
# FILE | |
fileMenu = Menu(mainMenu) | |
fileMenu.add_command(label="New Program", command=NewProgramCMD, accelerator="Ctrl+N") | |
fileMenu.add_separator() | |
fileMenu.add_command(label="Open Program", command=OpenProgramCMD, accelerator="Ctrl+O") | |
fileMenu.add_command(label="Save Program", command=SaveProgramCMD, accelerator="Ctrl+S") | |
fileMenu.add_command(label="Save Program As...", command=SaveProgramAsCMD, accelerator="Ctrl+Shift+S") | |
fileMenu.add_separator() | |
fileMenu.add_command(label="Exit", command=ExitCMD, accelerator="Ctrl+Q") | |
mainMenu.add_cascade(label="File", menu=fileMenu) | |
self.bind_all("<Control-n>", NewProgramCMD) | |
self.bind_all("<Control-o>", OpenProgramCMD) | |
self.bind_all("<Control-s>", SaveProgramCMD) | |
self.bind_all("<Control-Shift-s>", SaveProgramAsCMD) | |
self.bind_all("<Control-q>", ExitCMD) | |
self.pack(fill=BOTH) | |
def main(): | |
_tk = Tk() | |
_tk.geometry("640x512+20+20") | |
app = App(_tk) | |
_tk.mainloop() | |
if "__main__" in __name__: | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from bge import logic as G, types as T | |
import socket | |
import pickle | |
class ShaderBinder: | |
def __init__(self, obj): | |
self.ob = obj | |
self.shader = None | |
self.v = "void main() { gl_Position = ftransform(); }" | |
self.f = "void main() { gl_FragColor = vec4(1.0); }" | |
self.dummy = None | |
def isValid(self, v, f): | |
if self.dummy is not None: | |
self.dummy.delSource() | |
self.dummy.setSource(v, f, False) | |
seff.dummy.validate() | |
return self.dummy.isValid() | |
else: | |
return True | |
def update(self): | |
mesh = self.ob.meshes[0] | |
for mat in mesh.materials: | |
shader = mat.getShader() | |
if shader != None: | |
shader.delSource() | |
shader.setSource(self.v, self.f, True) | |
shader.validate() | |
self.dummy = shader | |
def setBoth(self, v, f): | |
if self.isValid(v, f): | |
if len(v.strip()) > 0: | |
self.v = v | |
if len(f.strip()) > 0: | |
self.f = f | |
self.update() | |
class Receiver(T.KX_GameObject): | |
def __init__(self, o): | |
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
self.sock.setblocking(False) | |
self.address = ("127.0.0.1", 8080) | |
self.sock.bind(self.address) | |
self.binder = ShaderBinder(self) | |
self.binder.update() | |
def __del__(self): | |
self.sock.close() | |
def receive_step(self): | |
try: | |
data, addr = self.sock.recvfrom(4096) | |
rec = None | |
if data: | |
rec = pickle.loads(data) | |
if rec["cmd"] == "GLE_START": | |
print("Connected to glEdit!") | |
elif rec["cmd"] == "GLE_QUIT": | |
print("Disconnected from glEdit.") | |
self.sock.close() | |
elif rec["cmd"] == "GLE_ALL_CHANGE": | |
vertex_shader = rec["data"][0] | |
fragment_shader = rec["data"][1] | |
self.binder.setBoth(vertex_shader, fragment_shader) | |
except: | |
pass | |
class Exit: | |
def __init__(self, t): | |
self.t = t | |
def __del__(self): | |
self.t.close() | |
def main(cont): | |
o = cont.owner | |
if "init" not in o: | |
o = Receiver(o) | |
G.__c = Exit(o.sock) | |
o["init"] = True | |
else: | |
o.receive_step() | |
if cont.sensors["EXIT"].positive: | |
o.sock.close() | |
G.endGame() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment