chiark / gitweb /
Add option to save GCode file. Re-center the objects on the bed when you add a new...
[cura.git] / Cura / gui / sceneView.py
1 from __future__ import absolute_import
2
3 import wx
4 import numpy
5 import time
6 import os
7 import traceback
8 import shutil
9
10 import OpenGL
11 OpenGL.ERROR_CHECKING = False
12 from OpenGL.GLU import *
13 from OpenGL.GL import *
14
15 from Cura.gui import printWindow
16 from Cura.util import profile
17 from Cura.util import meshLoader
18 from Cura.util import objectScene
19 from Cura.util import resources
20 from Cura.util import sliceEngine
21 from Cura.util import machineCom
22 from Cura.util import removableStorage
23 from Cura.gui.util import opengl
24 from Cura.gui.util import openglGui
25
26 class anim(object):
27         def __init__(self, start, end, runTime):
28                 self._start = start
29                 self._end = end
30                 self._startTime = time.time()
31                 self._runTime = runTime
32
33         def isDone(self):
34                 return time.time() > self._startTime + self._runTime
35
36         def getPosition(self):
37                 if self.isDone():
38                         return self._end
39                 f = (time.time() - self._startTime) / self._runTime
40                 ts = f*f
41                 tc = f*f*f
42                 #f = 6*tc*ts + -15*ts*ts + 10*tc
43                 f = tc + -3*ts + 3*f
44                 return self._start + (self._end - self._start) * f
45
46 class SceneView(openglGui.glGuiPanel):
47         def __init__(self, parent):
48                 super(SceneView, self).__init__(parent)
49
50                 self._yaw = 30
51                 self._pitch = 60
52                 self._zoom = 300
53                 self._scene = objectScene.Scene()
54                 self._objectShader = None
55                 self._focusObj = None
56                 self._selectedObj = None
57                 self._objColors = [None,None,None,None]
58                 self._mouseX = -1
59                 self._mouseY = -1
60                 self._mouseState = None
61                 self._viewTarget = numpy.array([0,0,0], numpy.float32)
62                 self._animView = None
63                 self._animZoom = None
64                 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
65                 self._platformMesh._drawOffset = numpy.array([0,0,0.5], numpy.float32)
66                 self._isSimpleMode = True
67
68                 self.openFileButton      = openglGui.glButton(self, 4, 'Load', (0,0), self.ShowLoadModel)
69                 self.printButton         = openglGui.glButton(self, 6, 'Print', (1,0), self.ShowPrintWindow)
70                 self.printButton.setDisabled(True)
71
72                 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
73                 self._sceneUpdateTimer = wx.Timer(self)
74                 self.Bind(wx.EVT_TIMER, lambda e : self._slicer.runSlicer(self._scene), self._sceneUpdateTimer)
75
76                 self.updateProfileToControls()
77                 wx.EVT_IDLE(self, self.OnIdle)
78
79         def ShowLoadModel(self, button):
80                 if button == 1:
81                         dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
82                         dlg.SetWildcard(meshLoader.wildcardFilter())
83                         if dlg.ShowModal() != wx.ID_OK:
84                                 dlg.Destroy()
85                                 return
86                         filename = dlg.GetPath()
87                         dlg.Destroy()
88                         if not(os.path.exists(filename)):
89                                 return False
90                         profile.putPreference('lastFile', filename)
91                         self.GetParent().GetParent().GetParent().addToModelMRU(filename)
92                         self.loadScene([filename])
93
94         def ShowPrintWindow(self, button):
95                 if button == 1:
96                         if machineCom.machineIsConnected():
97                                 printWindow.printFile(self._slicer.getGCodeFilename())
98                         elif len(removableStorage.getPossibleSDcardDrives()) > 0:
99                                 drives = removableStorage.getPossibleSDcardDrives()
100                                 if len(drives) > 1:
101                                         pass
102                         else:
103                                 defPath = profile.getPreference('lastFile')
104                                 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
105                                 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
106                                 dlg.SetFilename(defPath)
107                                 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
108                                 if dlg.ShowModal() != wx.ID_OK:
109                                         dlg.Destroy()
110                                         return
111                                 filename = dlg.GetPath()
112                                 dlg.Destroy()
113
114                                 shutil.copy(self._slicer.getGCodeFilename(), filename)
115
116         def OnIdle(self, e):
117                 if self._animView is not None or self._animZoom is not None:
118                         self.Refresh()
119                         return
120                 for obj in self._scene.objects():
121                         if obj._loadAnim is not None:
122                                 self.Refresh()
123                                 return
124
125         def sceneUpdated(self):
126                 self._sceneUpdateTimer.Start(1, True)
127                 self._slicer.abortSlicer()
128                 self.Refresh()
129
130         def _updateSliceProgress(self, progressValue, ready):
131                 self.printButton.setDisabled(not ready)
132                 self.printButton.setProgressBar(progressValue)
133                 self.Refresh()
134
135         def loadScene(self, fileList):
136                 for filename in fileList:
137                         try:
138                                 objList = meshLoader.loadMeshes(filename)
139                         except:
140                                 traceback.print_exc()
141                         else:
142                                 for obj in objList:
143                                         obj._loadAnim = anim(1, 0, 1.5)
144                                         self._scene.add(obj)
145                                         self._scene.centerAll()
146                                         self._selectObject(obj)
147                 self.sceneUpdated()
148
149         def _deleteObject(self, obj):
150                 if obj == self._selectedObj:
151                         self._selectedObj = None
152                 if obj == self._focusObj:
153                         self._focusObj = None
154                 self._scene.remove(obj)
155                 for m in obj._meshList:
156                         if m.vbo is not None:
157                                 self.glReleaseList.append(m.vbo)
158                 if self._isSimpleMode:
159                         self._scene.arrangeAll()
160                 self.sceneUpdated()
161
162         def _selectObject(self, obj, zoom = True):
163                 if obj != self._selectedObj:
164                         self._selectedObj = obj
165                 if zoom:
166                         newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
167                         self._animView = anim(self._viewTarget.copy(), newViewPos, 0.5)
168                         newZoom = obj.getBoundaryCircle() * 6
169                         if newZoom > numpy.max(self._machineSize) * 3:
170                                 newZoom = numpy.max(self._machineSize) * 3
171                         self._animZoom = anim(self._zoom, newZoom, 0.5)
172
173         def updateProfileToControls(self):
174                 oldSimpleMode = self._isSimpleMode
175                 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
176                 if self._isSimpleMode and not oldSimpleMode:
177                         self._scene.arrangeAll()
178                         self.sceneUpdated()
179                 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
180                 self._objColors[0] = profile.getPreferenceColour('model_colour')
181                 self._objColors[1] = profile.getPreferenceColour('model_colour2')
182                 self._objColors[2] = profile.getPreferenceColour('model_colour3')
183                 self._objColors[3] = profile.getPreferenceColour('model_colour4')
184                 self._scene.setMachineSize(self._machineSize)
185
186         def OnKeyChar(self, keyCode):
187                 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
188                         if self._selectedObj is not None:
189                                 self._deleteObject(self._selectedObj)
190                                 self.Refresh()
191
192                 if keyCode == wx.WXK_F3:
193                         shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
194
195         def ShaderUpdate(self, v, f):
196                 s = opengl.GLShader(v, f)
197                 if s.isValid():
198                         self._objectLoadShader.release()
199                         self._objectLoadShader = s
200                         for obj in self._scene.objects():
201                                 obj._loadAnim = anim(1, 0, 1.5)
202                         self.Refresh()
203
204         def OnMouseDown(self,e):
205                 self._mouseX = e.GetX()
206                 self._mouseY = e.GetY()
207                 self._mouseClick3DPos = self._mouse3Dpos
208                 if e.ButtonDClick():
209                         self._mouseState = 'doubleClick'
210                 else:
211                         self._mouseState = 'dragOrClick'
212                 if self._mouseState == 'dragOrClick':
213                         if e.Button == 1:
214                                 if self._focusObj is not None:
215                                         self._selectObject(self._focusObj, False)
216                                         self.Refresh()
217
218         def OnMouseUp(self, e):
219                 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
220                         return
221                 if self._mouseState == 'dragOrClick':
222                         if e.Button == 1:
223                                 if self._focusObj is not None:
224                                         self._selectObject(self._focusObj)
225                                 else:
226                                         self._selectedObj = None
227                                         self.Refresh()
228                         if e.Button == 3 and self._selectedObj == self._focusObj:
229                                 #menu = wx.Menu()
230                                 #menu.Append(-1, 'Test')
231                                 #self.PopupMenu(menu)
232                                 #menu.Destroy()
233                                 pass
234                 if self._mouseState == 'dragObject' and self._selectedObj is not None:
235                         self._scene.pushFree()
236                         self.sceneUpdated()
237                 self._mouseState = None
238
239         def OnMouseMotion(self,e):
240                 if e.Dragging() and self._mouseState is not None:
241                         self._mouseState = 'drag'
242                         if not e.LeftIsDown() and e.RightIsDown():
243                                 self._yaw += e.GetX() - self._mouseX
244                                 self._pitch -= e.GetY() - self._mouseY
245                                 if self._pitch > 170:
246                                         self._pitch = 170
247                                 if self._pitch < 10:
248                                         self._pitch = 10
249                         elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
250                                 self._zoom += e.GetY() - self._mouseY
251                                 if self._zoom < 1:
252                                         self._zoom = 1
253                                 if self._zoom > numpy.max(self._machineSize) * 3:
254                                         self._zoom = numpy.max(self._machineSize) * 3
255                         elif e.LeftIsDown() and self._selectedObj is not None and not self._isSimpleMode:
256                                 self._mouseState = 'dragObject'
257                                 z = max(0, self._mouseClick3DPos[2])
258                                 p0 = opengl.unproject(self._mouseX, self.viewport[1] + self.viewport[3] - self._mouseY, 0, self.modelMatrix, self.projMatrix, self.viewport)
259                                 p1 = opengl.unproject(self._mouseX, self.viewport[1] + self.viewport[3] - self._mouseY, 1, self.modelMatrix, self.projMatrix, self.viewport)
260                                 p2 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport)
261                                 p3 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport)
262                                 p0[2] -= z
263                                 p1[2] -= z
264                                 p2[2] -= z
265                                 p3[2] -= z
266                                 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
267                                 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
268                                 diff = cursorZ1 - cursorZ0
269                                 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
270
271                 self._mouseX = e.GetX()
272                 self._mouseY = e.GetY()
273
274         def _init3DView(self):
275                 # set viewing projection
276                 size = self.GetSize()
277                 glViewport(0, 0, size.GetWidth(), size.GetHeight())
278                 glLoadIdentity()
279
280                 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
281
282                 glDisable(GL_RESCALE_NORMAL)
283                 glDisable(GL_LIGHTING)
284                 glDisable(GL_LIGHT0)
285                 glEnable(GL_DEPTH_TEST)
286                 glDisable(GL_CULL_FACE)
287                 glDisable(GL_BLEND)
288                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
289
290                 glClearColor(0.8, 0.8, 0.8, 1.0)
291                 glClearStencil(0)
292                 glClearDepth(1.0)
293
294                 glMatrixMode(GL_PROJECTION)
295                 glLoadIdentity()
296                 aspect = float(size.GetWidth()) / float(size.GetHeight())
297                 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
298
299                 glMatrixMode(GL_MODELVIEW)
300                 glLoadIdentity()
301                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
302
303         def OnPaint(self,e):
304                 if machineCom.machineIsConnected():
305                         self.printButton._imageID = 6
306                         self.printButton._tooltip = 'Print'
307                 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
308                         self.printButton._imageID = 2
309                         self.printButton._tooltip = 'Toolpath to SD'
310                 else:
311                         self.printButton._imageID = 3
312                         self.printButton._tooltip = 'Save toolpath'
313
314                 if self._animView is not None:
315                         self._viewTarget = self._animView.getPosition()
316                         if self._animView.isDone():
317                                 self._animView = None
318                 if self._animZoom is not None:
319                         self._zoom = self._animZoom.getPosition()
320                         if self._animZoom.isDone():
321                                 self._animZoom = None
322                 if self._objectShader is None:
323                         self._objectShader = opengl.GLShader("""
324 uniform float cameraDistance;
325 varying float light_amount;
326
327 void main(void)
328 {
329     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
330     gl_FrontColor = gl_Color;
331
332         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
333         light_amount *= 1 - (length(gl_Position.xyz - vec3(0,0,cameraDistance)) / 1.5 / cameraDistance);
334         light_amount += 0.2;
335 }
336                         ""","""
337 uniform float cameraDistance;
338 varying float light_amount;
339
340 void main(void)
341 {
342         gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
343 }
344                         """)
345                         self._objectLoadShader = opengl.GLShader("""
346 uniform float cameraDistance;
347 uniform float intensity;
348 uniform float scale;
349 varying float light_amount;
350
351 void main(void)
352 {
353         vec4 tmp = gl_Vertex;
354     tmp.x += sin(tmp.z/5+intensity*30) * scale * intensity;
355     tmp.y += sin(tmp.z/3+intensity*40) * scale * intensity;
356     gl_Position = gl_ModelViewProjectionMatrix * tmp;
357     gl_FrontColor = gl_Color;
358
359         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
360         light_amount *= 1 - (length(gl_Position.xyz - vec3(0,0,cameraDistance)) / 1.5 / cameraDistance);
361         light_amount += 0.2;
362 }
363                         ""","""
364 uniform float cameraDistance;
365 uniform float intensity;
366 varying float light_amount;
367
368 void main(void)
369 {
370         gl_FragColor = vec4(gl_Color.xyz * light_amount, 1-intensity);
371 }
372                         """)
373                 self._init3DView()
374                 glTranslate(0,0,-self._zoom)
375                 glRotate(-self._pitch, 1,0,0)
376                 glRotate(self._yaw, 0,0,1)
377                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
378
379                 self.viewport = glGetIntegerv(GL_VIEWPORT)
380                 self.modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
381                 self.projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
382
383                 glClearColor(1,1,1,1)
384                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
385
386                 for n in xrange(0, len(self._scene.objects())):
387                         obj = self._scene.objects()[n]
388                         glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
389                         self._renderObject(obj)
390
391                 if self._mouseX > -1:
392                         n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
393                         if n < len(self._scene.objects()):
394                                 self._focusObj = self._scene.objects()[n]
395                         else:
396                                 self._focusObj = None
397                         f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
398                         self._mouse3Dpos = opengl.unproject(self._mouseX, self.viewport[1] + self.viewport[3] - self._mouseY, f, self.modelMatrix, self.projMatrix, self.viewport)
399
400                 self._init3DView()
401                 glTranslate(0,0,-self._zoom)
402                 glRotate(-self._pitch, 1,0,0)
403                 glRotate(self._yaw, 0,0,1)
404                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
405
406                 glStencilFunc(GL_ALWAYS, 1, 1)
407                 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
408                 self._objectShader.bind()
409                 self._objectShader.setUniform('cameraDistance', self._zoom)
410                 for obj in self._scene.objects():
411                         if obj._loadAnim is not None:
412                                 if obj._loadAnim.isDone():
413                                         obj._loadAnim = None
414                                 else:
415                                         continue
416                         col = self._objColors[0]
417                         if not self._scene.checkPlatform(obj):
418                                 col = [0.5,0.5,0.5,0.8]
419                         glDisable(GL_STENCIL_TEST)
420                         if self._selectedObj == obj:
421                                 col = map(lambda n: n * 1.5, col)
422                                 glEnable(GL_STENCIL_TEST)
423                         elif self._focusObj == obj:
424                                 col = map(lambda n: n * 1.2, col)
425                         elif self._focusObj is not None or  self._selectedObj is not None:
426                                 col = map(lambda n: n * 0.8, col)
427                         glColor4f(col[0], col[1], col[2], col[3])
428                         self._renderObject(obj)
429                 self._objectShader.unbind()
430
431                 glDisable(GL_STENCIL_TEST)
432                 glEnable(GL_BLEND)
433                 self._objectLoadShader.bind()
434                 self._objectLoadShader.setUniform('cameraDistance', self._zoom)
435                 glColor4f(0.2, 0.6, 1.0, 1.0)
436                 for obj in self._scene.objects():
437                         if obj._loadAnim is None:
438                                 continue
439                         self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
440                         self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
441                         self._renderObject(obj)
442                 self._objectLoadShader.unbind()
443                 glDisable(GL_BLEND)
444
445                 self._drawMachine()
446
447                 #Draw the outline of the selected object, on top of everything else except the GUI.
448                 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
449                         glDisable(GL_DEPTH_TEST)
450                         glEnable(GL_CULL_FACE)
451                         glEnable(GL_STENCIL_TEST)
452                         glStencilFunc(GL_EQUAL, 0, 255)
453                         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
454                         glLineWidth(2)
455                         glColor4f(1,1,1,0.5)
456                         self._renderObject(self._selectedObj)
457                         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
458                         glDisable(GL_STENCIL_TEST)
459                         glDisable(GL_CULL_FACE)
460                         glEnable(GL_DEPTH_TEST)
461
462         def _renderObject(self, obj):
463                 glPushMatrix()
464                 glTranslate(obj.getPosition()[0], obj.getPosition()[1], 0)
465                 offset = obj.getDrawOffset()
466                 glTranslate(-offset[0], -offset[1], -offset[2])
467                 for m in obj._meshList:
468                         if m.vbo is None:
469                                 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
470                         m.vbo.render()
471                 glPopMatrix()
472
473         def _drawMachine(self):
474                 glEnable(GL_CULL_FACE)
475                 glEnable(GL_BLEND)
476
477                 if profile.getPreference('machine_type') == 'ultimaker':
478                         glColor4f(1,1,1,0.5)
479                         self._objectShader.bind()
480                         self._objectShader.setUniform('cameraDistance', self._zoom)
481                         self._renderObject(self._platformMesh)
482                         self._objectShader.unbind()
483
484                 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
485                 v0 = [ size[0] / 2, size[1] / 2, size[2]]
486                 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
487                 v2 = [-size[0] / 2, size[1] / 2, size[2]]
488                 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
489                 v4 = [ size[0] / 2, size[1] / 2, 0]
490                 v5 = [ size[0] / 2,-size[1] / 2, 0]
491                 v6 = [-size[0] / 2, size[1] / 2, 0]
492                 v7 = [-size[0] / 2,-size[1] / 2, 0]
493
494                 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
495                 glEnableClientState(GL_VERTEX_ARRAY)
496                 glVertexPointer(3, GL_FLOAT, 3*4, vList)
497
498                 glColor4ub(5, 171, 231, 64)
499                 glDrawArrays(GL_QUADS, 0, 4)
500                 glColor4ub(5, 171, 231, 96)
501                 glDrawArrays(GL_QUADS, 4, 8)
502                 glColor4ub(5, 171, 231, 128)
503                 glDrawArrays(GL_QUADS, 12, 8)
504
505                 sx = self._machineSize[0]
506                 sy = self._machineSize[1]
507                 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
508                         for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
509                                 x1 = x * 10
510                                 x2 = x1 + 10
511                                 y1 = y * 10
512                                 y2 = y1 + 10
513                                 x1 = max(min(x1, sx/2), -sx/2)
514                                 y1 = max(min(y1, sy/2), -sy/2)
515                                 x2 = max(min(x2, sx/2), -sx/2)
516                                 y2 = max(min(y2, sy/2), -sy/2)
517                                 if (x & 1) == (y & 1):
518                                         glColor4ub(5, 171, 231, 127)
519                                 else:
520                                         glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
521                                 glBegin(GL_QUADS)
522                                 glVertex3f(x1, y1, -0.02)
523                                 glVertex3f(x2, y1, -0.02)
524                                 glVertex3f(x2, y2, -0.02)
525                                 glVertex3f(x1, y2, -0.02)
526                                 glEnd()
527
528                 glDisableClientState(GL_VERTEX_ARRAY)
529                 glDisable(GL_BLEND)
530                 glDisable(GL_CULL_FACE)
531
532 class shaderEditor(wx.Dialog):
533         def __init__(self, parent, callback, v, f):
534                 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
535                 self._callback = callback
536                 s = wx.BoxSizer(wx.VERTICAL)
537                 self.SetSizer(s)
538                 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
539                 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
540                 s.Add(self._vertex, 1, flag=wx.EXPAND)
541                 s.Add(self._fragment, 1, flag=wx.EXPAND)
542
543                 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
544                 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
545
546                 self.SetPosition(self.GetParent().GetPosition())
547                 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
548                 self.Show()
549
550         def OnText(self, e):
551                 self._callback(self._vertex.GetValue(), self._fragment.GetValue())