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