1 from __future__ import absolute_import
11 OpenGL.ERROR_CHECKING = False
12 from OpenGL.GLU import *
13 from OpenGL.GL import *
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.util import gcodeInterpreter
24 from Cura.gui.util import previewTools
25 from Cura.gui.util import opengl
26 from Cura.gui.util import openglGui
28 class SceneView(openglGui.glGuiPanel):
29 def __init__(self, parent):
30 super(SceneView, self).__init__(parent)
35 self._scene = objectScene.Scene()
38 self._objectShader = None
40 self._selectedObj = None
41 self._objColors = [None,None,None,None]
44 self._mouseState = None
45 self._viewTarget = numpy.array([0,0,0], numpy.float32)
48 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
49 self._platformMesh._drawOffset = numpy.array([0,0,1.5], numpy.float32)
50 self._isSimpleMode = True
53 self._modelMatrix = None
54 self._projMatrix = None
55 self.tempMatrix = None
57 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.ShowLoadModel)
58 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.ShowPrintWindow)
59 self.printButton.setDisabled(True)
62 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
63 self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
64 self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
66 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
67 self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
69 self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
70 self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
72 self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
73 self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
74 self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
76 self.rotateToolButton.setExpandArrow(True)
77 self.scaleToolButton.setExpandArrow(True)
78 self.mirrorToolButton.setExpandArrow(True)
80 self.scaleForm = openglGui.glFrame(self, (2, -2))
81 openglGui.glGuiLayoutGrid(self.scaleForm)
82 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
83 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
84 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
85 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
86 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
87 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
88 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
89 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
90 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
91 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
92 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
93 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
94 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
95 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
97 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,23], ['Normal', 'Layers'], (-1,0), self.OnViewChange)
98 self.layerSelect = openglGui.glSlider(self, 100, 0, 100, (-1,-2), lambda : self.QueueRefresh())
100 self.notification = openglGui.glNotification(self, (0, 0))
102 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
103 self._sceneUpdateTimer = wx.Timer(self)
104 self.Bind(wx.EVT_TIMER, lambda e : self._slicer.runSlicer(self._scene), self._sceneUpdateTimer)
105 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
109 self.updateToolButtons()
110 self.updateProfileToControls()
112 def ShowLoadModel(self, button):
114 dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
115 dlg.SetWildcard(meshLoader.wildcardFilter())
116 if dlg.ShowModal() != wx.ID_OK:
119 filename = dlg.GetPath()
121 if not(os.path.exists(filename)):
123 profile.putPreference('lastFile', filename)
124 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
125 self.loadScene([filename])
127 def ShowPrintWindow(self, button):
129 if machineCom.machineIsConnected():
130 printWindow.printFile(self._slicer.getGCodeFilename())
131 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
132 drives = removableStorage.getPossibleSDcardDrives()
137 filename = os.path.basename(profile.getPreference('lastFile'))
138 filename = filename[0:filename.rfind('.')] + '.gcode'
140 shutil.copy(self._slicer.getGCodeFilename(), drive[1] + filename)
142 self.notification.message("Failed to save to SD card")
144 self.notification.message("Saved as %s" % (drive[1] + filename))
146 defPath = profile.getPreference('lastFile')
147 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
148 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
149 dlg.SetFilename(defPath)
150 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
151 if dlg.ShowModal() != wx.ID_OK:
154 filename = dlg.GetPath()
158 shutil.copy(self._slicer.getGCodeFilename(), filename)
160 self.notification.message("Failed to save")
162 self.notification.message("Saved as %s" % (filename))
164 def OnToolSelect(self, button):
165 if self.rotateToolButton.getSelected():
166 self.tool = previewTools.toolRotate(self)
167 elif self.scaleToolButton.getSelected():
168 self.tool = previewTools.toolScale(self)
169 elif self.mirrorToolButton.getSelected():
170 self.tool = previewTools.toolNone(self)
172 self.tool = previewTools.toolNone(self)
173 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
174 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
175 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
176 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
177 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
178 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
179 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
180 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
182 def updateToolButtons(self):
183 if self._selectedObj is None:
187 self.rotateToolButton.setHidden(hidden)
188 self.scaleToolButton.setHidden(hidden)
189 self.mirrorToolButton.setHidden(hidden)
191 self.rotateToolButton.setSelected(False)
192 self.scaleToolButton.setSelected(False)
193 self.mirrorToolButton.setSelected(False)
196 def OnViewChange(self):
197 if self.viewSelection.getValue() == 1:
198 self.viewMode = 'gcode'
199 if self._gcode is not None:
200 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
201 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
202 self._selectObject(None)
204 self.viewMode = 'normal'
205 self.layerSelect.setHidden(self.viewMode != 'gcode')
206 self.openFileButton.setHidden(self.viewMode == 'gcode')
209 def OnRotateReset(self, button):
210 if self._selectedObj is None:
212 self._selectedObj.resetRotation()
213 self._scene.pushFree()
214 self._selectObject(self._selectedObj)
216 def OnLayFlat(self, button):
217 if self._selectedObj is None:
219 self._selectedObj.layFlat()
220 self._scene.pushFree()
221 self._selectObject(self._selectedObj)
223 def OnScaleReset(self, button):
224 if self._selectedObj is None:
226 self._selectedObj.resetScale()
228 def OnScaleMax(self, button):
229 if self._selectedObj is None:
231 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2)
232 self._scene.pushFree()
233 self._selectObject(self._selectedObj)
235 def OnMirror(self, axis):
236 if self._selectedObj is None:
238 self._selectedObj.mirror(axis)
241 def OnScaleEntry(self, value, axis):
242 if self._selectedObj is None:
248 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
249 self.updateProfileToControls()
250 self._scene.pushFree()
251 self._selectObject(self._selectedObj)
254 def OnScaleEntryMM(self, value, axis):
255 if self._selectedObj is None:
261 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
262 self.updateProfileToControls()
263 self._scene.pushFree()
264 self._selectObject(self._selectedObj)
267 def OnDeleteAll(self, e):
268 while len(self._scene.objects()) > 0:
269 self._deleteObject(self._scene.objects()[0])
270 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
272 def OnMultiply(self, e):
273 if self._focusObj is None:
276 dlg = wx.NumberEntryDialog(self, "How many copies need to be made?", "Copies", "Multiply", 1, 1, 100)
277 if dlg.ShowModal() != wx.ID_OK:
286 self._scene.add(newObj)
287 self._scene.centerAll()
288 if not self._scene.checkPlatform(newObj):
292 self._scene.remove(newObj)
293 self._scene.centerAll()
296 def OnSplitObject(self, e):
297 if self._focusObj is None:
299 self._scene.remove(self._focusObj)
300 for obj in self._focusObj.split():
302 self._scene.centerAll()
303 self._selectObject(None)
306 def OnMergeObjects(self, e):
307 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
309 self._scene.merge(self._selectedObj, self._focusObj)
312 def sceneUpdated(self):
313 self._sceneUpdateTimer.Start(1, True)
314 self._slicer.abortSlicer()
315 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
318 def _updateSliceProgress(self, progressValue, ready):
319 self.printButton.setDisabled(not ready)
320 self.printButton.setProgressBar(progressValue)
321 if self._gcode is not None:
323 for layerVBOlist in self._gcodeVBOs:
324 for vbo in layerVBOlist:
325 self.glReleaseList.append(vbo)
328 self._gcode = gcodeInterpreter.gcode()
329 self._gcode.progressCallback = self._gcodeLoadCallback
330 self._gcode.load(self._slicer.getGCodeFilename())
333 def _gcodeLoadCallback(self, progress):
334 if self._gcode is None:
336 if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
337 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
338 self.layerSelect.setValue(self.layerSelect.getMaxValue())
340 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
343 def loadScene(self, fileList):
344 for filename in fileList:
346 objList = meshLoader.loadMeshes(filename)
348 traceback.print_exc()
351 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
353 self._scene.centerAll()
354 self._selectObject(obj)
357 def _deleteObject(self, obj):
358 if obj == self._selectedObj:
359 self._selectObject(None)
360 if obj == self._focusObj:
361 self._focusObj = None
362 self._scene.remove(obj)
363 for m in obj._meshList:
364 if m.vbo is not None and m.vbo.decRef():
365 self.glReleaseList.append(m.vbo)
366 if self._isSimpleMode:
367 self._scene.arrangeAll()
370 def _selectObject(self, obj, zoom = True):
371 if obj != self._selectedObj:
372 self._selectedObj = obj
373 self.updateProfileToControls()
374 self.updateToolButtons()
375 if zoom and obj is not None:
376 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
377 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
378 newZoom = obj.getBoundaryCircle() * 6
379 if newZoom > numpy.max(self._machineSize) * 3:
380 newZoom = numpy.max(self._machineSize) * 3
381 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
383 def updateProfileToControls(self):
384 oldSimpleMode = self._isSimpleMode
385 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
386 if self._isSimpleMode and not oldSimpleMode:
387 self._scene.arrangeAll()
389 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
390 self._objColors[0] = profile.getPreferenceColour('model_colour')
391 self._objColors[1] = profile.getPreferenceColour('model_colour2')
392 self._objColors[2] = profile.getPreferenceColour('model_colour3')
393 self._objColors[3] = profile.getPreferenceColour('model_colour4')
394 self._scene.setMachineSize(self._machineSize)
395 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
396 self._scene.setHeadSize(profile.getPreferenceFloat('extruder_head_size_min_x'), profile.getPreferenceFloat('extruder_head_size_max_x'), profile.getPreferenceFloat('extruder_head_size_min_y'), profile.getPreferenceFloat('extruder_head_size_max_y'), profile.getPreferenceFloat('extruder_head_size_height'))
398 if self._selectedObj is not None:
399 scale = self._selectedObj.getScale()
400 size = self._selectedObj.getSize()
401 self.scaleXctrl.setValue(round(scale[0], 2))
402 self.scaleYctrl.setValue(round(scale[1], 2))
403 self.scaleZctrl.setValue(round(scale[2], 2))
404 self.scaleXmmctrl.setValue(round(size[0], 2))
405 self.scaleYmmctrl.setValue(round(size[1], 2))
406 self.scaleZmmctrl.setValue(round(size[2], 2))
408 def OnKeyChar(self, keyCode):
409 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
410 if self._selectedObj is not None:
411 self._deleteObject(self._selectedObj)
413 if keyCode == wx.WXK_UP:
414 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
416 elif keyCode == wx.WXK_DOWN:
417 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
419 elif keyCode == wx.WXK_PAGEUP:
420 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
422 elif keyCode == wx.WXK_PAGEDOWN:
423 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
426 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
427 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
429 def ShaderUpdate(self, v, f):
430 s = opengl.GLShader(v, f)
432 self._objectLoadShader.release()
433 self._objectLoadShader = s
434 for obj in self._scene.objects():
435 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
438 def OnMouseDown(self,e):
439 self._mouseX = e.GetX()
440 self._mouseY = e.GetY()
441 self._mouseClick3DPos = self._mouse3Dpos
442 self._mouseClickFocus = self._focusObj
444 self._mouseState = 'doubleClick'
446 self._mouseState = 'dragOrClick'
447 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
448 p0 -= self.getObjectCenterPos() - self._viewTarget
449 p1 -= self.getObjectCenterPos() - self._viewTarget
450 if self.tool.OnDragStart(p0, p1):
451 self._mouseState = 'tool'
452 if self._mouseState == 'dragOrClick':
453 if e.GetButton() == 1:
454 if self._focusObj is not None:
455 self._selectObject(self._focusObj, False)
458 def OnMouseUp(self, e):
459 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
461 if self._mouseState == 'dragOrClick':
462 if e.GetButton() == 1:
463 self._selectObject(self._focusObj)
464 if e.GetButton() == 3:
466 if self._focusObj is not None:
467 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
468 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
469 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
470 if self._selectedObj != self._focusObj and self._focusObj is not None:
471 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
472 if len(self._scene.objects()) > 0:
473 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
474 if menu.MenuItemCount > 0:
477 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
478 self._scene.pushFree()
480 elif self._mouseState == 'tool':
481 if self.tempMatrix is not None and self._selectedObj is not None:
482 self._selectedObj.applyMatrix(self.tempMatrix)
483 self._scene.pushFree()
484 self._selectObject(self._selectedObj)
485 self.tempMatrix = None
486 self.tool.OnDragEnd()
488 self._mouseState = None
490 def OnMouseMotion(self,e):
491 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
492 p0 -= self.getObjectCenterPos() - self._viewTarget
493 p1 -= self.getObjectCenterPos() - self._viewTarget
495 if e.Dragging() and self._mouseState is not None:
496 if self._mouseState == 'tool':
497 self.tool.OnDrag(p0, p1)
498 elif not e.LeftIsDown() and e.RightIsDown():
499 self._mouseState = 'drag'
500 self._yaw += e.GetX() - self._mouseX
501 self._pitch -= e.GetY() - self._mouseY
502 if self._pitch > 170:
506 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
507 self._mouseState = 'drag'
508 self._zoom += e.GetY() - self._mouseY
511 if self._zoom > numpy.max(self._machineSize) * 3:
512 self._zoom = numpy.max(self._machineSize) * 3
513 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus and not self._isSimpleMode:
514 self._mouseState = 'dragObject'
515 z = max(0, self._mouseClick3DPos[2])
516 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
517 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
522 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
523 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
524 diff = cursorZ1 - cursorZ0
525 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
526 if not e.Dragging() or self._mouseState != 'tool':
527 self.tool.OnMouseMove(p0, p1)
529 self._mouseX = e.GetX()
530 self._mouseY = e.GetY()
532 def OnMouseWheel(self, e):
533 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
534 delta = max(min(delta,4),-4)
535 self._zoom *= 1.0 - delta / 10.0
538 if self._zoom > numpy.max(self._machineSize) * 3:
539 self._zoom = numpy.max(self._machineSize) * 3
542 def getMouseRay(self, x, y):
543 if self._viewport is None:
544 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
545 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
546 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
547 p0 -= self._viewTarget
548 p1 -= self._viewTarget
551 def _init3DView(self):
552 # set viewing projection
553 size = self.GetSize()
554 glViewport(0, 0, size.GetWidth(), size.GetHeight())
557 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
559 glDisable(GL_RESCALE_NORMAL)
560 glDisable(GL_LIGHTING)
562 glEnable(GL_DEPTH_TEST)
563 glDisable(GL_CULL_FACE)
565 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
567 glClearColor(0.8, 0.8, 0.8, 1.0)
571 glMatrixMode(GL_PROJECTION)
573 aspect = float(size.GetWidth()) / float(size.GetHeight())
574 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
576 glMatrixMode(GL_MODELVIEW)
578 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
581 if machineCom.machineIsConnected():
582 self.printButton._imageID = 6
583 self.printButton._tooltip = 'Print'
584 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
585 self.printButton._imageID = 2
586 self.printButton._tooltip = 'Toolpath to SD'
588 self.printButton._imageID = 3
589 self.printButton._tooltip = 'Save toolpath'
591 if self._animView is not None:
592 self._viewTarget = self._animView.getPosition()
593 if self._animView.isDone():
594 self._animView = None
595 if self._animZoom is not None:
596 self._zoom = self._animZoom.getPosition()
597 if self._animZoom.isDone():
598 self._animZoom = None
599 if self._objectShader is None:
600 self._objectShader = opengl.GLShader("""
601 varying float light_amount;
605 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
606 gl_FrontColor = gl_Color;
608 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
612 varying float light_amount;
616 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
619 self._objectLoadShader = opengl.GLShader("""
620 uniform float intensity;
622 varying float light_amount;
626 vec4 tmp = gl_Vertex;
627 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
628 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
629 gl_Position = gl_ModelViewProjectionMatrix * tmp;
630 gl_FrontColor = gl_Color;
632 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
636 uniform float intensity;
637 varying float light_amount;
641 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
645 glTranslate(0,0,-self._zoom)
646 glRotate(-self._pitch, 1,0,0)
647 glRotate(self._yaw, 0,0,1)
648 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
650 self._viewport = glGetIntegerv(GL_VIEWPORT)
651 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
652 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
654 glClearColor(1,1,1,1)
655 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
657 if self.viewMode != 'gcode':
658 for n in xrange(0, len(self._scene.objects())):
659 obj = self._scene.objects()[n]
660 glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
661 self._renderObject(obj)
663 if self._mouseX > -1:
664 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
665 if n < len(self._scene.objects()):
666 self._focusObj = self._scene.objects()[n]
668 self._focusObj = None
669 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
670 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
671 self._mouse3Dpos -= self._viewTarget
674 glTranslate(0,0,-self._zoom)
675 glRotate(-self._pitch, 1,0,0)
676 glRotate(self._yaw, 0,0,1)
677 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
679 if self.viewMode == 'gcode':
680 if self._gcode is not None:
682 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
684 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
685 for n in xrange(0, drawUpTill):
686 c = 1.0 - float(drawUpTill - n) / 15
688 if len(self._gcodeVBOs) < n + 1:
689 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
690 if time.time() - t > 0.5:
693 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
695 self._gcodeVBOs[n][0].render(GL_LINES)
697 self._gcodeVBOs[n][1].render(GL_LINES)
698 glColor3f(c/2, c/2, 0.0)
699 self._gcodeVBOs[n][2].render(GL_LINES)
701 self._gcodeVBOs[n][3].render(GL_LINES)
702 self._gcodeVBOs[n][4].render(GL_LINES)
705 glStencilFunc(GL_ALWAYS, 1, 1)
706 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
707 self._objectShader.bind()
708 for obj in self._scene.objects():
709 if obj._loadAnim is not None:
710 if obj._loadAnim.isDone():
715 glDisable(GL_STENCIL_TEST)
716 if self._selectedObj == obj:
717 glEnable(GL_STENCIL_TEST)
718 if self._focusObj == obj:
720 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
722 if not self._scene.checkPlatform(obj):
723 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
724 self._renderObject(obj)
726 self._renderObject(obj, brightness)
727 self._objectShader.unbind()
729 glDisable(GL_STENCIL_TEST)
731 self._objectLoadShader.bind()
732 glColor4f(0.2, 0.6, 1.0, 1.0)
733 for obj in self._scene.objects():
734 if obj._loadAnim is None:
736 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
737 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
738 self._renderObject(obj)
739 self._objectLoadShader.unbind()
744 if self.viewMode == 'gcode':
747 #Draw the object box-shadow, so you can see where it will collide with other objects.
748 if self._selectedObj is not None and len(self._scene.objects()) > 1:
749 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
751 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0.0)
753 glEnable(GL_CULL_FACE)
754 glColor4f(0,0,0,0.12)
756 glVertex3f(-size[0], size[1], 0.1)
757 glVertex3f(-size[0], -size[1], 0.1)
758 glVertex3f( size[0], -size[1], 0.1)
759 glVertex3f( size[0], size[1], 0.1)
761 glDisable(GL_CULL_FACE)
764 #Draw the outline of the selected object, on top of everything else except the GUI.
765 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
766 glDisable(GL_DEPTH_TEST)
767 glEnable(GL_CULL_FACE)
768 glEnable(GL_STENCIL_TEST)
770 glStencilFunc(GL_EQUAL, 0, 255)
772 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
775 self._renderObject(self._selectedObj)
776 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
778 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
779 glDisable(GL_STENCIL_TEST)
780 glDisable(GL_CULL_FACE)
781 glEnable(GL_DEPTH_TEST)
783 if self._selectedObj is not None:
785 pos = self.getObjectCenterPos()
786 glTranslate(pos[0], pos[1], pos[2])
790 def _renderObject(self, obj, brightness = False):
792 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
794 if self.tempMatrix is not None and obj == self._selectedObj:
795 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
796 glMultMatrixf(tempMatrix)
798 offset = obj.getDrawOffset()
799 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
801 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
802 glMultMatrixf(tempMatrix)
805 for m in obj._meshList:
807 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
809 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
814 def _drawMachine(self):
815 glEnable(GL_CULL_FACE)
818 if profile.getPreference('machine_type') == 'ultimaker':
820 self._objectShader.bind()
821 self._renderObject(self._platformMesh)
822 self._objectShader.unbind()
824 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
825 v0 = [ size[0] / 2, size[1] / 2, size[2]]
826 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
827 v2 = [-size[0] / 2, size[1] / 2, size[2]]
828 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
829 v4 = [ size[0] / 2, size[1] / 2, 0]
830 v5 = [ size[0] / 2,-size[1] / 2, 0]
831 v6 = [-size[0] / 2, size[1] / 2, 0]
832 v7 = [-size[0] / 2,-size[1] / 2, 0]
834 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
835 glEnableClientState(GL_VERTEX_ARRAY)
836 glVertexPointer(3, GL_FLOAT, 3*4, vList)
838 glColor4ub(5, 171, 231, 64)
839 glDrawArrays(GL_QUADS, 0, 4)
840 glColor4ub(5, 171, 231, 96)
841 glDrawArrays(GL_QUADS, 4, 8)
842 glColor4ub(5, 171, 231, 128)
843 glDrawArrays(GL_QUADS, 12, 8)
845 sx = self._machineSize[0]
846 sy = self._machineSize[1]
847 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
848 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
853 x1 = max(min(x1, sx/2), -sx/2)
854 y1 = max(min(y1, sy/2), -sy/2)
855 x2 = max(min(x2, sx/2), -sx/2)
856 y2 = max(min(y2, sy/2), -sy/2)
857 if (x & 1) == (y & 1):
858 glColor4ub(5, 171, 231, 127)
860 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
862 glVertex3f(x1, y1, -0.02)
863 glVertex3f(x2, y1, -0.02)
864 glVertex3f(x2, y2, -0.02)
865 glVertex3f(x1, y2, -0.02)
868 glDisableClientState(GL_VERTEX_ARRAY)
870 glDisable(GL_CULL_FACE)
872 def _generateGCodeVBOs(self, layer):
874 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
875 pointList = numpy.zeros((0,3), numpy.float32)
877 if path.type == 'extrude' and path.pathType == extrudeType:
878 a = numpy.array(path.points, numpy.float32)
879 a = numpy.concatenate((a[:-1], a[1:]), 1)
880 a = a.reshape((len(a) * 2, 3))
881 pointList = numpy.concatenate((pointList, a))
882 ret.append(opengl.GLVBO(pointList))
885 def getObjectCenterPos(self):
886 if self._selectedObj is None:
887 return [0.0, 0.0, 0.0]
888 pos = self._selectedObj.getPosition()
889 size = self._selectedObj.getSize()
890 return [pos[0], pos[1], size[2]/2]
892 def getObjectBoundaryCircle(self):
893 if self._selectedObj is None:
895 return self._selectedObj.getBoundaryCircle()
897 def getObjectSize(self):
898 if self._selectedObj is None:
899 return [0.0, 0.0, 0.0]
900 return self._selectedObj.getSize()
902 def getObjectMatrix(self):
903 if self._selectedObj is None:
904 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
905 return self._selectedObj.getMatrix()
907 class shaderEditor(wx.Dialog):
908 def __init__(self, parent, callback, v, f):
909 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
910 self._callback = callback
911 s = wx.BoxSizer(wx.VERTICAL)
913 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
914 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
915 s.Add(self._vertex, 1, flag=wx.EXPAND)
916 s.Add(self._fragment, 1, flag=wx.EXPAND)
918 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
919 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
921 self.SetPosition(self.GetParent().GetPosition())
922 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
926 self._callback(self._vertex.GetValue(), self._fragment.GetValue())