chiark / gitweb /
Add object duplication.
[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 OnDuplicateObject(self, e):
213                 if self._selectedObj is None:
214                         return
215                 self._scene.add(self._selectedObj.copy())
216                 self._scene.centerAll()
217                 self.sceneUpdated()
218
219         def OnMergeObjects(self, e):
220                 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
221                         return
222                 self._scene.merge(self._selectedObj, self._focusObj)
223                 self.sceneUpdated()
224
225         def sceneUpdated(self):
226                 self._sceneUpdateTimer.Start(1, True)
227                 self._slicer.abortSlicer()
228                 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
229                 self.Refresh()
230
231         def _updateSliceProgress(self, progressValue, ready):
232                 self.printButton.setDisabled(not ready)
233                 self.printButton.setProgressBar(progressValue)
234                 self.Refresh()
235
236         def loadScene(self, fileList):
237                 for filename in fileList:
238                         try:
239                                 objList = meshLoader.loadMeshes(filename)
240                         except:
241                                 traceback.print_exc()
242                         else:
243                                 for obj in objList:
244                                         obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
245                                         self._scene.add(obj)
246                                         self._scene.centerAll()
247                                         self._selectObject(obj)
248                 self.sceneUpdated()
249
250         def _deleteObject(self, obj):
251                 if obj == self._selectedObj:
252                         self._selectedObj = None
253                 if obj == self._focusObj:
254                         self._focusObj = None
255                 self._scene.remove(obj)
256                 for m in obj._meshList:
257                         if m.vbo is not None and m.vbo.decRef():
258                                 self.glReleaseList.append(m.vbo)
259                 if self._isSimpleMode:
260                         self._scene.arrangeAll()
261                 self.sceneUpdated()
262
263         def _selectObject(self, obj, zoom = True):
264                 if obj != self._selectedObj:
265                         self._selectedObj = obj
266                         self.updateProfileToControls()
267                 if zoom:
268                         newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
269                         self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
270                         newZoom = obj.getBoundaryCircle() * 6
271                         if newZoom > numpy.max(self._machineSize) * 3:
272                                 newZoom = numpy.max(self._machineSize) * 3
273                         self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
274
275         def updateProfileToControls(self):
276                 oldSimpleMode = self._isSimpleMode
277                 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
278                 if self._isSimpleMode and not oldSimpleMode:
279                         self._scene.arrangeAll()
280                         self.sceneUpdated()
281                 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
282                 self._objColors[0] = profile.getPreferenceColour('model_colour')
283                 self._objColors[1] = profile.getPreferenceColour('model_colour2')
284                 self._objColors[2] = profile.getPreferenceColour('model_colour3')
285                 self._objColors[3] = profile.getPreferenceColour('model_colour4')
286                 self._scene.setMachineSize(self._machineSize)
287                 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
288
289                 if self._selectedObj is not None:
290                         scale = self._selectedObj.getScale()
291                         size = self._selectedObj.getSize()
292                         self.scaleXctrl.setValue(round(scale[0], 2))
293                         self.scaleYctrl.setValue(round(scale[1], 2))
294                         self.scaleZctrl.setValue(round(scale[2], 2))
295                         self.scaleXmmctrl.setValue(round(size[0], 2))
296                         self.scaleYmmctrl.setValue(round(size[1], 2))
297                         self.scaleZmmctrl.setValue(round(size[2], 2))
298
299         def OnKeyChar(self, keyCode):
300                 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
301                         if self._selectedObj is not None:
302                                 self._deleteObject(self._selectedObj)
303                                 self.Refresh()
304
305                 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
306                         shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
307
308         def ShaderUpdate(self, v, f):
309                 s = opengl.GLShader(v, f)
310                 if s.isValid():
311                         self._objectLoadShader.release()
312                         self._objectLoadShader = s
313                         for obj in self._scene.objects():
314                                 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
315                         self.Refresh()
316
317         def OnMouseDown(self,e):
318                 self._mouseX = e.GetX()
319                 self._mouseY = e.GetY()
320                 self._mouseClick3DPos = self._mouse3Dpos
321                 self._mouseClickFocus = self._focusObj
322                 if e.ButtonDClick():
323                         self._mouseState = 'doubleClick'
324                 else:
325                         self._mouseState = 'dragOrClick'
326                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
327                 p0 -= self.getObjectCenterPos() - self._viewTarget
328                 p1 -= self.getObjectCenterPos() - self._viewTarget
329                 if self.tool.OnDragStart(p0, p1):
330                         self._mouseState = 'tool'
331                 if self._mouseState == 'dragOrClick':
332                         if e.GetButton() == 1:
333                                 if self._focusObj is not None:
334                                         self._selectObject(self._focusObj, False)
335                                         self.Refresh()
336
337         def OnMouseUp(self, e):
338                 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
339                         return
340                 if self._mouseState == 'dragOrClick':
341                         if e.GetButton() == 1:
342                                 if self._focusObj is not None:
343                                         self._selectObject(self._focusObj)
344                                 else:
345                                         self._selectedObj = None
346                                         self.Refresh()
347                         if e.GetButton() == 3:
348                                 if self._selectedObj == self._focusObj:
349                                         menu = wx.Menu()
350                                         self.Bind(wx.EVT_MENU, self.OnDuplicateObject, menu.Append(-1, 'Duplicate'))
351                                         self.PopupMenu(menu)
352                                         menu.Destroy()
353                                 if self._selectedObj != self._focusObj and self._focusObj is not None:
354                                         menu = wx.Menu()
355                                         self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Merge'))
356                                         self.PopupMenu(menu)
357                                         menu.Destroy()
358                 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
359                         self._scene.pushFree()
360                         self.sceneUpdated()
361                 elif self._mouseState == 'tool':
362                         if self.tempMatrix is not None and self._selectedObj is not None:
363                                 self._selectedObj.applyMatrix(self.tempMatrix)
364                         self.tempMatrix = None
365                         self.tool.OnDragEnd()
366                         self.sceneUpdated()
367                 self._mouseState = None
368
369         def OnMouseMotion(self,e):
370                 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
371                 p0 -= self.getObjectCenterPos() - self._viewTarget
372                 p1 -= self.getObjectCenterPos() - self._viewTarget
373
374                 if e.Dragging() and self._mouseState is not None:
375                         if self._mouseState == 'tool':
376                                 self.tool.OnDrag(p0, p1)
377                         elif not e.LeftIsDown() and e.RightIsDown():
378                                 self._yaw += e.GetX() - self._mouseX
379                                 self._pitch -= e.GetY() - self._mouseY
380                                 if self._pitch > 170:
381                                         self._pitch = 170
382                                 if self._pitch < 10:
383                                         self._pitch = 10
384                         elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
385                                 self._zoom += e.GetY() - self._mouseY
386                                 if self._zoom < 1:
387                                         self._zoom = 1
388                                 if self._zoom > numpy.max(self._machineSize) * 3:
389                                         self._zoom = numpy.max(self._machineSize) * 3
390                         elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus and not self._isSimpleMode:
391                                 self._mouseState = 'dragObject'
392                                 z = max(0, self._mouseClick3DPos[2])
393                                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
394                                 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
395                                 p0[2] -= z
396                                 p1[2] -= z
397                                 p2[2] -= z
398                                 p3[2] -= z
399                                 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
400                                 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
401                                 diff = cursorZ1 - cursorZ0
402                                 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
403                 if not e.Dragging() or self._mouseState != 'tool':
404                         self.tool.OnMouseMove(p0, p1)
405
406                 self._mouseX = e.GetX()
407                 self._mouseY = e.GetY()
408
409         def OnMouseWheel(self, e):
410                 self._zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
411                 if self._zoom < 1.0:
412                         self._zoom = 1.0
413                 if self._zoom > numpy.max(self._machineSize) * 3:
414                         self._zoom = numpy.max(self._machineSize) * 3
415                 self.Refresh()
416
417         def getMouseRay(self, x, y):
418                 if self._viewport is None:
419                         return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
420                 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
421                 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
422                 p0 -= self._viewTarget
423                 p1 -= self._viewTarget
424                 return p0, p1
425
426         def _init3DView(self):
427                 # set viewing projection
428                 size = self.GetSize()
429                 glViewport(0, 0, size.GetWidth(), size.GetHeight())
430                 glLoadIdentity()
431
432                 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
433
434                 glDisable(GL_RESCALE_NORMAL)
435                 glDisable(GL_LIGHTING)
436                 glDisable(GL_LIGHT0)
437                 glEnable(GL_DEPTH_TEST)
438                 glDisable(GL_CULL_FACE)
439                 glDisable(GL_BLEND)
440                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
441
442                 glClearColor(0.8, 0.8, 0.8, 1.0)
443                 glClearStencil(0)
444                 glClearDepth(1.0)
445
446                 glMatrixMode(GL_PROJECTION)
447                 glLoadIdentity()
448                 aspect = float(size.GetWidth()) / float(size.GetHeight())
449                 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
450
451                 glMatrixMode(GL_MODELVIEW)
452                 glLoadIdentity()
453                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
454
455         def OnPaint(self,e):
456                 if machineCom.machineIsConnected():
457                         self.printButton._imageID = 6
458                         self.printButton._tooltip = 'Print'
459                 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
460                         self.printButton._imageID = 2
461                         self.printButton._tooltip = 'Toolpath to SD'
462                 else:
463                         self.printButton._imageID = 3
464                         self.printButton._tooltip = 'Save toolpath'
465
466                 if self._animView is not None:
467                         self._viewTarget = self._animView.getPosition()
468                         if self._animView.isDone():
469                                 self._animView = None
470                 if self._animZoom is not None:
471                         self._zoom = self._animZoom.getPosition()
472                         if self._animZoom.isDone():
473                                 self._animZoom = None
474                 if self._objectShader is None:
475                         self._objectShader = opengl.GLShader("""
476 varying float light_amount;
477
478 void main(void)
479 {
480     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
481     gl_FrontColor = gl_Color;
482
483         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
484         light_amount += 0.2;
485 }
486                         ""","""
487 varying float light_amount;
488
489 void main(void)
490 {
491         gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
492 }
493                         """)
494                         self._objectLoadShader = opengl.GLShader("""
495 uniform float intensity;
496 uniform float scale;
497 varying float light_amount;
498
499 void main(void)
500 {
501         vec4 tmp = gl_Vertex;
502     tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
503     tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
504     gl_Position = gl_ModelViewProjectionMatrix * tmp;
505     gl_FrontColor = gl_Color;
506
507         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
508         light_amount += 0.2;
509 }
510                         ""","""
511 uniform float intensity;
512 varying float light_amount;
513
514 void main(void)
515 {
516         gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
517 }
518                         """)
519                 self._init3DView()
520                 glTranslate(0,0,-self._zoom)
521                 glRotate(-self._pitch, 1,0,0)
522                 glRotate(self._yaw, 0,0,1)
523                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
524
525                 self._viewport = glGetIntegerv(GL_VIEWPORT)
526                 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
527                 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
528
529                 glClearColor(1,1,1,1)
530                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
531
532                 for n in xrange(0, len(self._scene.objects())):
533                         obj = self._scene.objects()[n]
534                         glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
535                         self._renderObject(obj)
536
537                 if self._mouseX > -1:
538                         n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
539                         if n < len(self._scene.objects()):
540                                 self._focusObj = self._scene.objects()[n]
541                         else:
542                                 self._focusObj = None
543                         f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
544                         self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
545                         self._mouse3Dpos -= self._viewTarget
546
547                 self._init3DView()
548                 glTranslate(0,0,-self._zoom)
549                 glRotate(-self._pitch, 1,0,0)
550                 glRotate(self._yaw, 0,0,1)
551                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
552
553                 glStencilFunc(GL_ALWAYS, 1, 1)
554                 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
555                 self._objectShader.bind()
556                 for obj in self._scene.objects():
557                         if obj._loadAnim is not None:
558                                 if obj._loadAnim.isDone():
559                                         obj._loadAnim = None
560                                 else:
561                                         continue
562                         brightness = 1.0
563                         glDisable(GL_STENCIL_TEST)
564                         if self._selectedObj == obj:
565                                 glEnable(GL_STENCIL_TEST)
566                         if self._focusObj == obj:
567                                 brightness = 1.2
568                         elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
569                                 brightness = 0.8
570                         if not self._scene.checkPlatform(obj):
571                                 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
572                                 self._renderObject(obj)
573                         else:
574                                 self._renderObject(obj, brightness)
575                 self._objectShader.unbind()
576
577                 glDisable(GL_STENCIL_TEST)
578                 glEnable(GL_BLEND)
579                 self._objectLoadShader.bind()
580                 glColor4f(0.2, 0.6, 1.0, 1.0)
581                 for obj in self._scene.objects():
582                         if obj._loadAnim is None:
583                                 continue
584                         self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
585                         self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
586                         self._renderObject(obj)
587                 self._objectLoadShader.unbind()
588                 glDisable(GL_BLEND)
589
590                 self._drawMachine()
591
592                 #Draw the object box-shadow, so you can see where it will collide with other objects.
593                 if self._selectedObj is not None and len(self._scene.objects()) > 1:
594                         size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
595                         glPushMatrix()
596                         glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0.0)
597                         glEnable(GL_BLEND)
598                         glEnable(GL_CULL_FACE)
599                         glColor4f(0,0,0,0.12)
600                         glBegin(GL_QUADS)
601                         glVertex3f(-size[0],  size[1], 0)
602                         glVertex3f(-size[0], -size[1], 0)
603                         glVertex3f( size[0], -size[1], 0)
604                         glVertex3f( size[0],  size[1], 0)
605                         glEnd()
606                         glDisable(GL_CULL_FACE)
607                         glPopMatrix()
608
609                 #Draw the outline of the selected object, on top of everything else except the GUI.
610                 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
611                         glDisable(GL_DEPTH_TEST)
612                         glEnable(GL_CULL_FACE)
613                         glEnable(GL_STENCIL_TEST)
614                         glDisable(GL_BLEND)
615                         glStencilFunc(GL_EQUAL, 0, 255)
616
617                         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
618                         glLineWidth(2)
619                         glColor4f(1,1,1,0.5)
620                         self._renderObject(self._selectedObj)
621                         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
622
623                         glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
624                         glDisable(GL_STENCIL_TEST)
625                         glDisable(GL_CULL_FACE)
626                         glEnable(GL_DEPTH_TEST)
627
628                 if self._selectedObj is not None:
629                         glPushMatrix()
630                         pos = self.getObjectCenterPos()
631                         glTranslate(pos[0], pos[1], pos[2])
632                         self.tool.OnDraw()
633                         glPopMatrix()
634
635         def _renderObject(self, obj, brightness = False):
636                 glPushMatrix()
637                 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
638
639                 if self.tempMatrix is not None and obj == self._selectedObj:
640                         tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
641                         glMultMatrixf(tempMatrix)
642
643                 offset = obj.getDrawOffset()
644                 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
645
646                 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
647                 glMultMatrixf(tempMatrix)
648
649                 n = 0
650                 for m in obj._meshList:
651                         if m.vbo is None:
652                                 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
653                         if brightness:
654                                 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
655                                 n += 1
656                         m.vbo.render()
657                 glPopMatrix()
658
659         def _drawMachine(self):
660                 glEnable(GL_CULL_FACE)
661                 glEnable(GL_BLEND)
662
663                 if profile.getPreference('machine_type') == 'ultimaker':
664                         glColor4f(1,1,1,0.5)
665                         self._objectShader.bind()
666                         self._renderObject(self._platformMesh)
667                         self._objectShader.unbind()
668
669                 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
670                 v0 = [ size[0] / 2, size[1] / 2, size[2]]
671                 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
672                 v2 = [-size[0] / 2, size[1] / 2, size[2]]
673                 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
674                 v4 = [ size[0] / 2, size[1] / 2, 0]
675                 v5 = [ size[0] / 2,-size[1] / 2, 0]
676                 v6 = [-size[0] / 2, size[1] / 2, 0]
677                 v7 = [-size[0] / 2,-size[1] / 2, 0]
678
679                 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
680                 glEnableClientState(GL_VERTEX_ARRAY)
681                 glVertexPointer(3, GL_FLOAT, 3*4, vList)
682
683                 glColor4ub(5, 171, 231, 64)
684                 glDrawArrays(GL_QUADS, 0, 4)
685                 glColor4ub(5, 171, 231, 96)
686                 glDrawArrays(GL_QUADS, 4, 8)
687                 glColor4ub(5, 171, 231, 128)
688                 glDrawArrays(GL_QUADS, 12, 8)
689
690                 sx = self._machineSize[0]
691                 sy = self._machineSize[1]
692                 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
693                         for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
694                                 x1 = x * 10
695                                 x2 = x1 + 10
696                                 y1 = y * 10
697                                 y2 = y1 + 10
698                                 x1 = max(min(x1, sx/2), -sx/2)
699                                 y1 = max(min(y1, sy/2), -sy/2)
700                                 x2 = max(min(x2, sx/2), -sx/2)
701                                 y2 = max(min(y2, sy/2), -sy/2)
702                                 if (x & 1) == (y & 1):
703                                         glColor4ub(5, 171, 231, 127)
704                                 else:
705                                         glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
706                                 glBegin(GL_QUADS)
707                                 glVertex3f(x1, y1, -0.02)
708                                 glVertex3f(x2, y1, -0.02)
709                                 glVertex3f(x2, y2, -0.02)
710                                 glVertex3f(x1, y2, -0.02)
711                                 glEnd()
712
713                 glDisableClientState(GL_VERTEX_ARRAY)
714                 glDisable(GL_BLEND)
715                 glDisable(GL_CULL_FACE)
716
717         def getObjectCenterPos(self):
718                 if self._selectedObj is None:
719                         return [0.0, 0.0, 0.0]
720                 pos = self._selectedObj.getPosition()
721                 size = self._selectedObj.getSize()
722                 return [pos[0], pos[1], size[2]/2]
723
724         def getObjectBoundaryCircle(self):
725                 if self._selectedObj is None:
726                         return 0.0
727                 return self._selectedObj.getBoundaryCircle()
728
729         def getObjectSize(self):
730                 if self._selectedObj is None:
731                         return [0.0, 0.0, 0.0]
732                 return self._selectedObj.getSize()
733
734         def getObjectMatrix(self):
735                 if self._selectedObj is None:
736                         return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
737                 return self._selectedObj.getMatrix()
738
739 class shaderEditor(wx.Dialog):
740         def __init__(self, parent, callback, v, f):
741                 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
742                 self._callback = callback
743                 s = wx.BoxSizer(wx.VERTICAL)
744                 self.SetSizer(s)
745                 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
746                 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
747                 s.Add(self._vertex, 1, flag=wx.EXPAND)
748                 s.Add(self._fragment, 1, flag=wx.EXPAND)
749
750                 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
751                 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
752
753                 self.SetPosition(self.GetParent().GetPosition())
754                 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
755                 self.Show()
756
757         def OnText(self, e):
758                 self._callback(self._vertex.GetValue(), self._fragment.GetValue())