chiark / gitweb /
Some code cleanup and fixing the minecraft import.
[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 previewTools
24 from Cura.gui.util import opengl
25 from Cura.gui.util import openglGui
26
27 class anim(object):
28         def __init__(self, start, end, runTime):
29                 self._start = start
30                 self._end = end
31                 self._startTime = time.time()
32                 self._runTime = runTime
33
34         def isDone(self):
35                 return time.time() > self._startTime + self._runTime
36
37         def getPosition(self):
38                 if self.isDone():
39                         return self._end
40                 f = (time.time() - self._startTime) / self._runTime
41                 ts = f*f
42                 tc = f*f*f
43                 #f = 6*tc*ts + -15*ts*ts + 10*tc
44                 f = tc + -3*ts + 3*f
45                 return self._start + (self._end - self._start) * f
46
47 class SceneView(openglGui.glGuiPanel):
48         def __init__(self, parent):
49                 super(SceneView, self).__init__(parent)
50
51                 self._yaw = 30
52                 self._pitch = 60
53                 self._zoom = 300
54                 self._scene = objectScene.Scene()
55                 self._objectShader = None
56                 self._focusObj = None
57                 self._selectedObj = None
58                 self._objColors = [None,None,None,None]
59                 self._mouseX = -1
60                 self._mouseY = -1
61                 self._mouseState = None
62                 self._viewTarget = numpy.array([0,0,0], numpy.float32)
63                 self._animView = None
64                 self._animZoom = None
65                 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
66                 self._platformMesh._drawOffset = numpy.array([0,0,1.5], numpy.float32)
67                 self._isSimpleMode = True
68
69                 self._viewport = None
70                 self._modelMatrix = None
71                 self._projMatrix = None
72                 self.tempMatrix = None
73
74                 self.openFileButton      = openglGui.glButton(self, 4, 'Load', (0,0), self.ShowLoadModel)
75                 self.printButton         = openglGui.glButton(self, 6, 'Print', (1,0), self.ShowPrintWindow)
76                 self.printButton.setDisabled(True)
77
78                 group = []
79                 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
80                 self.scaleToolButton  = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
81                 self.mirrorToolButton  = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
82
83                 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
84                 self.layFlatButton       = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
85
86                 self.resetScaleButton    = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
87                 self.scaleMaxButton      = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
88
89                 self.mirrorXButton       = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
90                 self.mirrorYButton       = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
91                 self.mirrorZButton       = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
92
93                 self.rotateToolButton.setExpandArrow(True)
94                 self.scaleToolButton.setExpandArrow(True)
95                 self.mirrorToolButton.setExpandArrow(True)
96
97                 self.scaleForm = openglGui.glFrame(self, (2, -2))
98                 openglGui.glGuiLayoutGrid(self.scaleForm)
99                 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
100                 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
101                 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
102                 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
103                 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
104                 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
105                 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
106                 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
107                 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
108                 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
109                 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
110                 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
111                 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
112                 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
113
114                 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
115                 self._sceneUpdateTimer = wx.Timer(self)
116                 self.Bind(wx.EVT_TIMER, lambda e : self._slicer.runSlicer(self._scene), self._sceneUpdateTimer)
117                 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
118
119                 self.OnToolSelect(0)
120                 self.updateProfileToControls()
121                 wx.EVT_IDLE(self, self.OnIdle)
122
123         def ShowLoadModel(self, button):
124                 if button == 1:
125                         dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
126                         dlg.SetWildcard(meshLoader.wildcardFilter())
127                         if dlg.ShowModal() != wx.ID_OK:
128                                 dlg.Destroy()
129                                 return
130                         filename = dlg.GetPath()
131                         dlg.Destroy()
132                         if not(os.path.exists(filename)):
133                                 return False
134                         profile.putPreference('lastFile', filename)
135                         self.GetParent().GetParent().GetParent().addToModelMRU(filename)
136                         self.loadScene([filename])
137
138         def ShowPrintWindow(self, button):
139                 if button == 1:
140                         if machineCom.machineIsConnected():
141                                 printWindow.printFile(self._slicer.getGCodeFilename())
142                         elif len(removableStorage.getPossibleSDcardDrives()) > 0:
143                                 drives = removableStorage.getPossibleSDcardDrives()
144                                 if len(drives) > 1:
145                                         drive = drives[0]
146                                 else:
147                                         drive = drives[0]
148                                 filename = os.path.basename(profile.getPreference('lastFile'))
149                                 filename = filename[0:filename.rfind('.')] + '.gcode'
150                                 shutil.copy(self._slicer.getGCodeFilename(), drive[1] + filename)
151                         else:
152                                 defPath = profile.getPreference('lastFile')
153                                 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
154                                 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
155                                 dlg.SetFilename(defPath)
156                                 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
157                                 if dlg.ShowModal() != wx.ID_OK:
158                                         dlg.Destroy()
159                                         return
160                                 filename = dlg.GetPath()
161                                 dlg.Destroy()
162
163                                 shutil.copy(self._slicer.getGCodeFilename(), filename)
164
165         def OnIdle(self, e):
166                 if self._animView is not None or self._animZoom is not None:
167                         self.Refresh()
168                         return
169                 for obj in self._scene.objects():
170                         if obj._loadAnim is not None:
171                                 self.Refresh()
172                                 return
173
174         def OnToolSelect(self, button):
175                 if self.rotateToolButton.getSelected():
176                         self.tool = previewTools.toolRotate(self)
177                 elif self.scaleToolButton.getSelected():
178                         self.tool = previewTools.toolScale(self)
179                 elif self.mirrorToolButton.getSelected():
180                         self.tool = previewTools.toolNone(self)
181                 else:
182                         self.tool = previewTools.toolNone(self)
183                 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
184                 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
185                 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
186                 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
187                 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
188                 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
189                 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
190                 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
191
192         def OnRotateReset(self, button):
193                 if self._selectedObj is None:
194                         return
195                 pass
196
197         def OnLayFlat(self, button):
198                 if self._selectedObj is None:
199                         return
200                 pass
201
202         def OnScaleReset(self, button):
203                 if self._selectedObj is None:
204                         return
205                 pass
206
207         def OnScaleMax(self, button):
208                 if self._selectedObj is None:
209                         return
210                 pass
211
212         def OnMirror(self, axis):
213                 if self._selectedObj is None:
214                         return
215                 self._selectedObj.mirror(axis)
216
217         def sceneUpdated(self):
218                 self._sceneUpdateTimer.Start(1, True)
219                 self._slicer.abortSlicer()
220                 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
221                 self.Refresh()
222
223         def _updateSliceProgress(self, progressValue, ready):
224                 self.printButton.setDisabled(not ready)
225                 self.printButton.setProgressBar(progressValue)
226                 self.Refresh()
227
228         def loadScene(self, fileList):
229                 for filename in fileList:
230                         try:
231                                 objList = meshLoader.loadMeshes(filename)
232                         except:
233                                 traceback.print_exc()
234                         else:
235                                 for obj in objList:
236                                         obj._loadAnim = anim(1, 0, 1.5)
237                                         self._scene.add(obj)
238                                         self._scene.centerAll()
239                                         self._selectObject(obj)
240                 self.sceneUpdated()
241
242         def _deleteObject(self, obj):
243                 if obj == self._selectedObj:
244                         self._selectedObj = None
245                 if obj == self._focusObj:
246                         self._focusObj = None
247                 self._scene.remove(obj)
248                 for m in obj._meshList:
249                         if m.vbo is not None:
250                                 self.glReleaseList.append(m.vbo)
251                 if self._isSimpleMode:
252                         self._scene.arrangeAll()
253                 self.sceneUpdated()
254
255         def _selectObject(self, obj, zoom = True):
256                 if obj != self._selectedObj:
257                         self._selectedObj = obj
258                         scale = self._selectedObj.getScale()
259                         size = self._selectedObj.getSize()
260                         self.scaleXctrl.setValue(round(scale[0], 2))
261                         self.scaleYctrl.setValue(round(scale[1], 2))
262                         self.scaleZctrl.setValue(round(scale[2], 2))
263                         self.scaleXmmctrl.setValue(round(size[0], 2))
264                         self.scaleYmmctrl.setValue(round(size[1], 2))
265                         self.scaleZmmctrl.setValue(round(size[2], 2))
266                 if zoom:
267                         newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
268                         self._animView = anim(self._viewTarget.copy(), newViewPos, 0.5)
269                         newZoom = obj.getBoundaryCircle() * 6
270                         if newZoom > numpy.max(self._machineSize) * 3:
271                                 newZoom = numpy.max(self._machineSize) * 3
272                         self._animZoom = anim(self._zoom, newZoom, 0.5)
273
274         def updateProfileToControls(self):
275                 oldSimpleMode = self._isSimpleMode
276                 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
277                 if self._isSimpleMode and not oldSimpleMode:
278                         self._scene.arrangeAll()
279                         self.sceneUpdated()
280                 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
281                 self._objColors[0] = profile.getPreferenceColour('model_colour')
282                 self._objColors[1] = profile.getPreferenceColour('model_colour2')
283                 self._objColors[2] = profile.getPreferenceColour('model_colour3')
284                 self._objColors[3] = profile.getPreferenceColour('model_colour4')
285                 self._scene.setMachineSize(self._machineSize)
286
287         def OnKeyChar(self, keyCode):
288                 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
289                         if self._selectedObj is not None:
290                                 self._deleteObject(self._selectedObj)
291                                 self.Refresh()
292
293                 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
294                         shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
295
296         def ShaderUpdate(self, v, f):
297                 s = opengl.GLShader(v, f)
298                 if s.isValid():
299                         self._objectLoadShader.release()
300                         self._objectLoadShader = s
301                         for obj in self._scene.objects():
302                                 obj._loadAnim = anim(1, 0, 1.5)
303                         self.Refresh()
304
305         def OnMouseDown(self,e):
306                 self._mouseX = e.GetX()
307                 self._mouseY = e.GetY()
308                 self._mouseClick3DPos = self._mouse3Dpos
309                 self._mouseClickFocus = self._focusObj
310                 if e.ButtonDClick():
311                         self._mouseState = 'doubleClick'
312                 else:
313                         self._mouseState = 'dragOrClick'
314                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
315                 p0 -= self.getObjectCenterPos() - self._viewTarget
316                 p1 -= self.getObjectCenterPos() - self._viewTarget
317                 if self.tool.OnDragStart(p0, p1):
318                         self._mouseState = 'tool'
319                 if self._mouseState == 'dragOrClick':
320                         if e.GetButton() == 1:
321                                 if self._focusObj is not None:
322                                         self._selectObject(self._focusObj, False)
323                                         self.Refresh()
324
325         def OnMouseUp(self, e):
326                 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
327                         return
328                 if self._mouseState == 'dragOrClick':
329                         if e.GetButton() == 1:
330                                 if self._focusObj is not None:
331                                         self._selectObject(self._focusObj)
332                                 else:
333                                         self._selectedObj = None
334                                         self.Refresh()
335                         if e.GetButton() == 3 and self._selectedObj == self._focusObj:
336                                 #menu = wx.Menu()
337                                 #menu.Append(-1, 'Test')
338                                 #self.PopupMenu(menu)
339                                 #menu.Destroy()
340                                 pass
341                 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
342                         self._scene.pushFree()
343                         self.sceneUpdated()
344                 elif self._mouseState == 'tool':
345                         if self.tempMatrix is not None and self._selectedObj is not None:
346                                 self._selectedObj.applyMatrix(self.tempMatrix)
347                         self.tempMatrix = None
348                         self.tool.OnDragEnd()
349                         self.sceneUpdated()
350                 self._mouseState = None
351
352         def OnMouseMotion(self,e):
353                 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
354                 p0 -= self.getObjectCenterPos() - self._viewTarget
355                 p1 -= self.getObjectCenterPos() - self._viewTarget
356
357                 if e.Dragging() and self._mouseState is not None:
358                         if self._mouseState == 'tool':
359                                 self.tool.OnDrag(p0, p1)
360                         elif not e.LeftIsDown() and e.RightIsDown():
361                                 self._yaw += e.GetX() - self._mouseX
362                                 self._pitch -= e.GetY() - self._mouseY
363                                 if self._pitch > 170:
364                                         self._pitch = 170
365                                 if self._pitch < 10:
366                                         self._pitch = 10
367                         elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
368                                 self._zoom += e.GetY() - self._mouseY
369                                 if self._zoom < 1:
370                                         self._zoom = 1
371                                 if self._zoom > numpy.max(self._machineSize) * 3:
372                                         self._zoom = numpy.max(self._machineSize) * 3
373                         elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus and not self._isSimpleMode:
374                                 self._mouseState = 'dragObject'
375                                 z = max(0, self._mouseClick3DPos[2])
376                                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
377                                 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
378                                 p0[2] -= z
379                                 p1[2] -= z
380                                 p2[2] -= z
381                                 p3[2] -= z
382                                 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
383                                 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
384                                 diff = cursorZ1 - cursorZ0
385                                 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
386                 if not e.Dragging() or self._mouseState != 'tool':
387                         self.tool.OnMouseMove(p0, p1)
388
389                 self._mouseX = e.GetX()
390                 self._mouseY = e.GetY()
391
392         def OnMouseWheel(self, e):
393                 self._zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
394                 if self._zoom < 1.0:
395                         self._zoom = 1.0
396                 if self._zoom > numpy.max(self._machineSize) * 3:
397                         self._zoom = numpy.max(self._machineSize) * 3
398                 self.Refresh()
399
400         def getMouseRay(self, x, y):
401                 if self._viewport is None:
402                         return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
403                 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
404                 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
405                 p0 -= self._viewTarget
406                 p1 -= self._viewTarget
407                 return p0, p1
408
409         def _init3DView(self):
410                 # set viewing projection
411                 size = self.GetSize()
412                 glViewport(0, 0, size.GetWidth(), size.GetHeight())
413                 glLoadIdentity()
414
415                 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
416
417                 glDisable(GL_RESCALE_NORMAL)
418                 glDisable(GL_LIGHTING)
419                 glDisable(GL_LIGHT0)
420                 glEnable(GL_DEPTH_TEST)
421                 glDisable(GL_CULL_FACE)
422                 glDisable(GL_BLEND)
423                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
424
425                 glClearColor(0.8, 0.8, 0.8, 1.0)
426                 glClearStencil(0)
427                 glClearDepth(1.0)
428
429                 glMatrixMode(GL_PROJECTION)
430                 glLoadIdentity()
431                 aspect = float(size.GetWidth()) / float(size.GetHeight())
432                 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
433
434                 glMatrixMode(GL_MODELVIEW)
435                 glLoadIdentity()
436                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
437
438         def OnPaint(self,e):
439                 if machineCom.machineIsConnected():
440                         self.printButton._imageID = 6
441                         self.printButton._tooltip = 'Print'
442                 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
443                         self.printButton._imageID = 2
444                         self.printButton._tooltip = 'Toolpath to SD'
445                 else:
446                         self.printButton._imageID = 3
447                         self.printButton._tooltip = 'Save toolpath'
448
449                 if self._animView is not None:
450                         self._viewTarget = self._animView.getPosition()
451                         if self._animView.isDone():
452                                 self._animView = None
453                 if self._animZoom is not None:
454                         self._zoom = self._animZoom.getPosition()
455                         if self._animZoom.isDone():
456                                 self._animZoom = None
457                 if self._objectShader is None:
458                         self._objectShader = opengl.GLShader("""
459 varying float light_amount;
460
461 void main(void)
462 {
463     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
464     gl_FrontColor = gl_Color;
465
466         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
467         light_amount += 0.2;
468 }
469                         ""","""
470 varying float light_amount;
471
472 void main(void)
473 {
474         gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
475 }
476                         """)
477                         self._objectLoadShader = opengl.GLShader("""
478 uniform float intensity;
479 uniform float scale;
480 varying float light_amount;
481
482 void main(void)
483 {
484         vec4 tmp = gl_Vertex;
485     tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
486     tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
487     gl_Position = gl_ModelViewProjectionMatrix * tmp;
488     gl_FrontColor = gl_Color;
489
490         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
491         light_amount += 0.2;
492 }
493                         ""","""
494 uniform float intensity;
495 varying float light_amount;
496
497 void main(void)
498 {
499         gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
500 }
501                         """)
502                 self._init3DView()
503                 glTranslate(0,0,-self._zoom)
504                 glRotate(-self._pitch, 1,0,0)
505                 glRotate(self._yaw, 0,0,1)
506                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
507
508                 self._viewport = glGetIntegerv(GL_VIEWPORT)
509                 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
510                 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
511
512                 glClearColor(1,1,1,1)
513                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
514
515                 for n in xrange(0, len(self._scene.objects())):
516                         obj = self._scene.objects()[n]
517                         glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
518                         self._renderObject(obj)
519
520                 if self._mouseX > -1:
521                         n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
522                         if n < len(self._scene.objects()):
523                                 self._focusObj = self._scene.objects()[n]
524                         else:
525                                 self._focusObj = None
526                         f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
527                         self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
528                         self._mouse3Dpos -= self._viewTarget
529
530                 self._init3DView()
531                 glTranslate(0,0,-self._zoom)
532                 glRotate(-self._pitch, 1,0,0)
533                 glRotate(self._yaw, 0,0,1)
534                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
535
536                 glStencilFunc(GL_ALWAYS, 1, 1)
537                 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
538                 self._objectShader.bind()
539                 for obj in self._scene.objects():
540                         if obj._loadAnim is not None:
541                                 if obj._loadAnim.isDone():
542                                         obj._loadAnim = None
543                                 else:
544                                         continue
545                         col = self._objColors[0]
546                         if not self._scene.checkPlatform(obj):
547                                 col = [0.5,0.5,0.5,0.8]
548                         glDisable(GL_STENCIL_TEST)
549                         if self._selectedObj == obj:
550                                 glEnable(GL_STENCIL_TEST)
551                         if self._focusObj == obj:
552                                 col = map(lambda n: n * 1.2, col)
553                         elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
554                                 col = map(lambda n: n * 0.8, col)
555                         glColor4f(col[0], col[1], col[2], col[3])
556                         self._renderObject(obj)
557                 self._objectShader.unbind()
558
559                 glDisable(GL_STENCIL_TEST)
560                 glEnable(GL_BLEND)
561                 self._objectLoadShader.bind()
562                 glColor4f(0.2, 0.6, 1.0, 1.0)
563                 for obj in self._scene.objects():
564                         if obj._loadAnim is None:
565                                 continue
566                         self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
567                         self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
568                         self._renderObject(obj)
569                 self._objectLoadShader.unbind()
570                 glDisable(GL_BLEND)
571
572                 self._drawMachine()
573
574                 #Draw the object box-shadow, so you can see where it will collide with other objects.
575                 if self._selectedObj is not None and len(self._scene.objects()) > 1:
576                         size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
577                         glPushMatrix()
578                         glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0.0)
579                         glEnable(GL_BLEND)
580                         glEnable(GL_CULL_FACE)
581                         glColor4f(0,0,0,0.12)
582                         glBegin(GL_QUADS)
583                         glVertex3f(-size[0],  size[1], 0)
584                         glVertex3f(-size[0], -size[1], 0)
585                         glVertex3f( size[0], -size[1], 0)
586                         glVertex3f( size[0],  size[1], 0)
587                         glEnd()
588                         glDisable(GL_CULL_FACE)
589                         glPopMatrix()
590
591                 #Draw the outline of the selected object, on top of everything else except the GUI.
592                 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
593                         glDisable(GL_DEPTH_TEST)
594                         glEnable(GL_CULL_FACE)
595                         glEnable(GL_STENCIL_TEST)
596                         glStencilFunc(GL_EQUAL, 0, 255)
597
598                         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
599                         glLineWidth(2)
600                         glColor4f(1,1,1,0.5)
601                         self._renderObject(self._selectedObj)
602                         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
603
604                         glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
605                         glDisable(GL_STENCIL_TEST)
606                         glDisable(GL_CULL_FACE)
607                         glEnable(GL_DEPTH_TEST)
608
609                 if self._selectedObj is not None:
610                         glPushMatrix()
611                         pos = self.getObjectCenterPos()
612                         glTranslate(pos[0], pos[1], pos[2])
613                         self.tool.OnDraw()
614                         glPopMatrix()
615
616         def _renderObject(self, obj):
617                 glPushMatrix()
618                 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
619
620                 if self.tempMatrix is not None and obj == self._selectedObj:
621                         tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
622                         glMultMatrixf(tempMatrix)
623
624                 offset = obj.getDrawOffset()
625                 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
626
627                 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
628                 glMultMatrixf(tempMatrix)
629
630                 for m in obj._meshList:
631                         if m.vbo is None:
632                                 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
633                         m.vbo.render()
634                 glPopMatrix()
635
636         def _drawMachine(self):
637                 glEnable(GL_CULL_FACE)
638                 glEnable(GL_BLEND)
639
640                 if profile.getPreference('machine_type') == 'ultimaker':
641                         glColor4f(1,1,1,0.5)
642                         self._objectShader.bind()
643                         self._renderObject(self._platformMesh)
644                         self._objectShader.unbind()
645
646                 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
647                 v0 = [ size[0] / 2, size[1] / 2, size[2]]
648                 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
649                 v2 = [-size[0] / 2, size[1] / 2, size[2]]
650                 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
651                 v4 = [ size[0] / 2, size[1] / 2, 0]
652                 v5 = [ size[0] / 2,-size[1] / 2, 0]
653                 v6 = [-size[0] / 2, size[1] / 2, 0]
654                 v7 = [-size[0] / 2,-size[1] / 2, 0]
655
656                 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
657                 glEnableClientState(GL_VERTEX_ARRAY)
658                 glVertexPointer(3, GL_FLOAT, 3*4, vList)
659
660                 glColor4ub(5, 171, 231, 64)
661                 glDrawArrays(GL_QUADS, 0, 4)
662                 glColor4ub(5, 171, 231, 96)
663                 glDrawArrays(GL_QUADS, 4, 8)
664                 glColor4ub(5, 171, 231, 128)
665                 glDrawArrays(GL_QUADS, 12, 8)
666
667                 sx = self._machineSize[0]
668                 sy = self._machineSize[1]
669                 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
670                         for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
671                                 x1 = x * 10
672                                 x2 = x1 + 10
673                                 y1 = y * 10
674                                 y2 = y1 + 10
675                                 x1 = max(min(x1, sx/2), -sx/2)
676                                 y1 = max(min(y1, sy/2), -sy/2)
677                                 x2 = max(min(x2, sx/2), -sx/2)
678                                 y2 = max(min(y2, sy/2), -sy/2)
679                                 if (x & 1) == (y & 1):
680                                         glColor4ub(5, 171, 231, 127)
681                                 else:
682                                         glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
683                                 glBegin(GL_QUADS)
684                                 glVertex3f(x1, y1, -0.02)
685                                 glVertex3f(x2, y1, -0.02)
686                                 glVertex3f(x2, y2, -0.02)
687                                 glVertex3f(x1, y2, -0.02)
688                                 glEnd()
689
690                 glDisableClientState(GL_VERTEX_ARRAY)
691                 glDisable(GL_BLEND)
692                 glDisable(GL_CULL_FACE)
693
694         def getObjectCenterPos(self):
695                 if self._selectedObj is None:
696                         return [0.0, 0.0, 0.0]
697                 pos = self._selectedObj.getPosition()
698                 size = self._selectedObj.getSize()
699                 return [pos[0], pos[1], size[2]/2]
700
701         def getObjectBoundaryCircle(self):
702                 if self._selectedObj is None:
703                         return 0.0
704                 return self._selectedObj.getBoundaryCircle()
705
706         def getObjectSize(self):
707                 if self._selectedObj is None:
708                         return [0.0, 0.0, 0.0]
709                 return self._selectedObj.getSize()
710
711         def getObjectMatrix(self):
712                 if self._selectedObj is None:
713                         return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
714                 return self._selectedObj.getMatrix()
715
716 class shaderEditor(wx.Dialog):
717         def __init__(self, parent, callback, v, f):
718                 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
719                 self._callback = callback
720                 s = wx.BoxSizer(wx.VERTICAL)
721                 self.SetSizer(s)
722                 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
723                 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
724                 s.Add(self._vertex, 1, flag=wx.EXPAND)
725                 s.Add(self._fragment, 1, flag=wx.EXPAND)
726
727                 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
728                 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
729
730                 self.SetPosition(self.GetParent().GetPosition())
731                 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
732                 self.Show()
733
734         def OnText(self, e):
735                 self._callback(self._vertex.GetValue(), self._fragment.GetValue())