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