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