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()
134 dlg = wx.SingleChoiceDialog(self, "Select SD drive", "Multiple removable drives have been found,\nplease select your SD card drive", map(lambda n: n[0], drives))
135 if dlg.ShowModal() != wx.ID_OK:
138 drive = drives[dlg.GetSelection()]
142 filename = os.path.basename(profile.getPreference('lastFile'))
143 filename = filename[0:filename.rfind('.')] + '.gcode'
145 shutil.copy(self._slicer.getGCodeFilename(), drive[1] + filename)
147 self.notification.message("Failed to save to SD card")
149 self.notification.message("Saved as %s" % (drive[1] + filename))
151 self._showSaveGCode()
154 self.Bind(wx.EVT_MENU, lambda e: printWindow.printFile(self._slicer.getGCodeFilename()), menu.Append(-1, 'Print with USB'))
155 self.Bind(wx.EVT_MENU, lambda e: self._showSaveGCode(), menu.Append(-1, 'Save GCode...'))
159 def _showSaveGCode(self):
160 defPath = profile.getPreference('lastFile')
161 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
162 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
163 dlg.SetFilename(defPath)
164 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
165 if dlg.ShowModal() != wx.ID_OK:
168 filename = dlg.GetPath()
172 shutil.copy(self._slicer.getGCodeFilename(), filename)
174 self.notification.message("Failed to save")
176 self.notification.message("Saved as %s" % (filename))
178 def OnToolSelect(self, button):
179 if self.rotateToolButton.getSelected():
180 self.tool = previewTools.toolRotate(self)
181 elif self.scaleToolButton.getSelected():
182 self.tool = previewTools.toolScale(self)
183 elif self.mirrorToolButton.getSelected():
184 self.tool = previewTools.toolNone(self)
186 self.tool = previewTools.toolNone(self)
187 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
188 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
189 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
190 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
191 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
192 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
193 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
194 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
196 def updateToolButtons(self):
197 if self._selectedObj is None:
201 self.rotateToolButton.setHidden(hidden)
202 self.scaleToolButton.setHidden(hidden)
203 self.mirrorToolButton.setHidden(hidden)
205 self.rotateToolButton.setSelected(False)
206 self.scaleToolButton.setSelected(False)
207 self.mirrorToolButton.setSelected(False)
210 def OnViewChange(self):
211 if self.viewSelection.getValue() == 1:
212 self.viewMode = 'gcode'
213 if self._gcode is not None:
214 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
215 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
216 self._selectObject(None)
218 self.viewMode = 'normal'
219 self.layerSelect.setHidden(self.viewMode != 'gcode')
220 self.openFileButton.setHidden(self.viewMode == 'gcode')
223 def OnRotateReset(self, button):
224 if self._selectedObj is None:
226 self._selectedObj.resetRotation()
227 self._scene.pushFree()
228 self._selectObject(self._selectedObj)
230 def OnLayFlat(self, button):
231 if self._selectedObj is None:
233 self._selectedObj.layFlat()
234 self._scene.pushFree()
235 self._selectObject(self._selectedObj)
237 def OnScaleReset(self, button):
238 if self._selectedObj is None:
240 self._selectedObj.resetScale()
242 def OnScaleMax(self, button):
243 if self._selectedObj is None:
245 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2)
246 self._scene.pushFree()
247 self._selectObject(self._selectedObj)
249 def OnMirror(self, axis):
250 if self._selectedObj is None:
252 self._selectedObj.mirror(axis)
255 def OnScaleEntry(self, value, axis):
256 if self._selectedObj is None:
262 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
263 self.updateProfileToControls()
264 self._scene.pushFree()
265 self._selectObject(self._selectedObj)
268 def OnScaleEntryMM(self, value, axis):
269 if self._selectedObj is None:
275 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
276 self.updateProfileToControls()
277 self._scene.pushFree()
278 self._selectObject(self._selectedObj)
281 def OnDeleteAll(self, e):
282 while len(self._scene.objects()) > 0:
283 self._deleteObject(self._scene.objects()[0])
284 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
286 def OnMultiply(self, e):
287 if self._focusObj is None:
290 dlg = wx.NumberEntryDialog(self, "How many copies need to be made?", "Copies", "Multiply", 1, 1, 100)
291 if dlg.ShowModal() != wx.ID_OK:
300 self._scene.add(newObj)
301 self._scene.centerAll()
302 if not self._scene.checkPlatform(newObj):
306 self._scene.remove(newObj)
307 self._scene.centerAll()
310 def OnSplitObject(self, e):
311 if self._focusObj is None:
313 self._scene.remove(self._focusObj)
314 for obj in self._focusObj.split():
316 self._scene.centerAll()
317 self._selectObject(None)
320 def OnMergeObjects(self, e):
321 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
323 self._scene.merge(self._selectedObj, self._focusObj)
326 def sceneUpdated(self):
327 self._sceneUpdateTimer.Start(1, True)
328 self._slicer.abortSlicer()
329 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
332 def _updateSliceProgress(self, progressValue, ready):
333 self.printButton.setDisabled(not ready)
334 self.printButton.setProgressBar(progressValue)
335 if self._gcode is not None:
337 for layerVBOlist in self._gcodeVBOs:
338 for vbo in layerVBOlist:
339 self.glReleaseList.append(vbo)
342 self._gcode = gcodeInterpreter.gcode()
343 self._gcode.progressCallback = self._gcodeLoadCallback
344 self._gcode.load(self._slicer.getGCodeFilename())
347 def _gcodeLoadCallback(self, progress):
348 if self._gcode is None:
350 if self.layerSelect.getValue() == self.layerSelect.getMaxValue():
351 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
352 self.layerSelect.setValue(self.layerSelect.getMaxValue())
354 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
357 def loadScene(self, fileList):
358 for filename in fileList:
360 objList = meshLoader.loadMeshes(filename)
362 traceback.print_exc()
365 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
367 self._scene.centerAll()
368 self._selectObject(obj)
371 def _deleteObject(self, obj):
372 if obj == self._selectedObj:
373 self._selectObject(None)
374 if obj == self._focusObj:
375 self._focusObj = None
376 self._scene.remove(obj)
377 for m in obj._meshList:
378 if m.vbo is not None and m.vbo.decRef():
379 self.glReleaseList.append(m.vbo)
380 if self._isSimpleMode:
381 self._scene.arrangeAll()
384 def _selectObject(self, obj, zoom = True):
385 if obj != self._selectedObj:
386 self._selectedObj = obj
387 self.updateProfileToControls()
388 self.updateToolButtons()
389 if zoom and obj is not None:
390 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
391 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
392 newZoom = obj.getBoundaryCircle() * 6
393 if newZoom > numpy.max(self._machineSize) * 3:
394 newZoom = numpy.max(self._machineSize) * 3
395 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
397 def updateProfileToControls(self):
398 oldSimpleMode = self._isSimpleMode
399 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
400 if self._isSimpleMode and not oldSimpleMode:
401 self._scene.arrangeAll()
403 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
404 self._objColors[0] = profile.getPreferenceColour('model_colour')
405 self._objColors[1] = profile.getPreferenceColour('model_colour2')
406 self._objColors[2] = profile.getPreferenceColour('model_colour3')
407 self._objColors[3] = profile.getPreferenceColour('model_colour4')
408 self._scene.setMachineSize(self._machineSize)
409 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
410 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'))
412 if self._selectedObj is not None:
413 scale = self._selectedObj.getScale()
414 size = self._selectedObj.getSize()
415 self.scaleXctrl.setValue(round(scale[0], 2))
416 self.scaleYctrl.setValue(round(scale[1], 2))
417 self.scaleZctrl.setValue(round(scale[2], 2))
418 self.scaleXmmctrl.setValue(round(size[0], 2))
419 self.scaleYmmctrl.setValue(round(size[1], 2))
420 self.scaleZmmctrl.setValue(round(size[2], 2))
422 def OnKeyChar(self, keyCode):
423 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
424 if self._selectedObj is not None:
425 self._deleteObject(self._selectedObj)
427 if keyCode == wx.WXK_UP:
428 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
430 elif keyCode == wx.WXK_DOWN:
431 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
433 elif keyCode == wx.WXK_PAGEUP:
434 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
436 elif keyCode == wx.WXK_PAGEDOWN:
437 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
440 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
441 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
443 def ShaderUpdate(self, v, f):
444 s = opengl.GLShader(v, f)
446 self._objectLoadShader.release()
447 self._objectLoadShader = s
448 for obj in self._scene.objects():
449 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
452 def OnMouseDown(self,e):
453 self._mouseX = e.GetX()
454 self._mouseY = e.GetY()
455 self._mouseClick3DPos = self._mouse3Dpos
456 self._mouseClickFocus = self._focusObj
458 self._mouseState = 'doubleClick'
460 self._mouseState = 'dragOrClick'
461 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
462 p0 -= self.getObjectCenterPos() - self._viewTarget
463 p1 -= self.getObjectCenterPos() - self._viewTarget
464 if self.tool.OnDragStart(p0, p1):
465 self._mouseState = 'tool'
466 if self._mouseState == 'dragOrClick':
467 if e.GetButton() == 1:
468 if self._focusObj is not None:
469 self._selectObject(self._focusObj, False)
472 def OnMouseUp(self, e):
473 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
475 if self._mouseState == 'dragOrClick':
476 if e.GetButton() == 1:
477 self._selectObject(self._focusObj)
478 if e.GetButton() == 3:
480 if self._focusObj is not None:
481 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
482 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
483 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
484 if self._selectedObj != self._focusObj and self._focusObj is not None and int(profile.getPreference('extruder_amount')) > 1:
485 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
486 if len(self._scene.objects()) > 0:
487 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
488 if menu.MenuItemCount > 0:
491 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
492 self._scene.pushFree()
494 elif self._mouseState == 'tool':
495 if self.tempMatrix is not None and self._selectedObj is not None:
496 self._selectedObj.applyMatrix(self.tempMatrix)
497 self._scene.pushFree()
498 self._selectObject(self._selectedObj)
499 self.tempMatrix = None
500 self.tool.OnDragEnd()
502 self._mouseState = None
504 def OnMouseMotion(self,e):
505 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
506 p0 -= self.getObjectCenterPos() - self._viewTarget
507 p1 -= self.getObjectCenterPos() - self._viewTarget
509 if e.Dragging() and self._mouseState is not None:
510 if self._mouseState == 'tool':
511 self.tool.OnDrag(p0, p1)
512 elif not e.LeftIsDown() and e.RightIsDown():
513 self._mouseState = 'drag'
514 self._yaw += e.GetX() - self._mouseX
515 self._pitch -= e.GetY() - self._mouseY
516 if self._pitch > 170:
520 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
521 self._mouseState = 'drag'
522 self._zoom += e.GetY() - self._mouseY
525 if self._zoom > numpy.max(self._machineSize) * 3:
526 self._zoom = numpy.max(self._machineSize) * 3
527 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus and not self._isSimpleMode:
528 self._mouseState = 'dragObject'
529 z = max(0, self._mouseClick3DPos[2])
530 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
531 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
536 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
537 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
538 diff = cursorZ1 - cursorZ0
539 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
540 if not e.Dragging() or self._mouseState != 'tool':
541 self.tool.OnMouseMove(p0, p1)
543 self._mouseX = e.GetX()
544 self._mouseY = e.GetY()
546 def OnMouseWheel(self, e):
547 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
548 delta = max(min(delta,4),-4)
549 self._zoom *= 1.0 - delta / 10.0
552 if self._zoom > numpy.max(self._machineSize) * 3:
553 self._zoom = numpy.max(self._machineSize) * 3
556 def getMouseRay(self, x, y):
557 if self._viewport is None:
558 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
559 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
560 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
561 p0 -= self._viewTarget
562 p1 -= self._viewTarget
565 def _init3DView(self):
566 # set viewing projection
567 size = self.GetSize()
568 glViewport(0, 0, size.GetWidth(), size.GetHeight())
571 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
573 glDisable(GL_RESCALE_NORMAL)
574 glDisable(GL_LIGHTING)
576 glEnable(GL_DEPTH_TEST)
577 glDisable(GL_CULL_FACE)
579 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
581 glClearColor(0.8, 0.8, 0.8, 1.0)
585 glMatrixMode(GL_PROJECTION)
587 aspect = float(size.GetWidth()) / float(size.GetHeight())
588 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
590 glMatrixMode(GL_MODELVIEW)
592 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
595 if machineCom.machineIsConnected():
596 self.printButton._imageID = 6
597 self.printButton._tooltip = 'Print'
598 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
599 self.printButton._imageID = 2
600 self.printButton._tooltip = 'Toolpath to SD'
602 self.printButton._imageID = 3
603 self.printButton._tooltip = 'Save toolpath'
605 if self._animView is not None:
606 self._viewTarget = self._animView.getPosition()
607 if self._animView.isDone():
608 self._animView = None
609 if self._animZoom is not None:
610 self._zoom = self._animZoom.getPosition()
611 if self._animZoom.isDone():
612 self._animZoom = None
613 if self._objectShader is None:
614 self._objectShader = opengl.GLShader("""
615 varying float light_amount;
619 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
620 gl_FrontColor = gl_Color;
622 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
626 varying float light_amount;
630 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
633 self._objectLoadShader = opengl.GLShader("""
634 uniform float intensity;
636 varying float light_amount;
640 vec4 tmp = gl_Vertex;
641 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
642 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
643 gl_Position = gl_ModelViewProjectionMatrix * tmp;
644 gl_FrontColor = gl_Color;
646 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
650 uniform float intensity;
651 varying float light_amount;
655 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
659 glTranslate(0,0,-self._zoom)
660 glRotate(-self._pitch, 1,0,0)
661 glRotate(self._yaw, 0,0,1)
662 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
664 self._viewport = glGetIntegerv(GL_VIEWPORT)
665 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
666 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
668 glClearColor(1,1,1,1)
669 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
671 if self.viewMode != 'gcode':
672 for n in xrange(0, len(self._scene.objects())):
673 obj = self._scene.objects()[n]
674 glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
675 self._renderObject(obj)
677 if self._mouseX > -1:
678 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
679 if n < len(self._scene.objects()):
680 self._focusObj = self._scene.objects()[n]
682 self._focusObj = None
683 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
684 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
685 self._mouse3Dpos -= self._viewTarget
688 glTranslate(0,0,-self._zoom)
689 glRotate(-self._pitch, 1,0,0)
690 glRotate(self._yaw, 0,0,1)
691 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
693 if self.viewMode == 'gcode':
694 if self._gcode is not None:
696 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
698 drawUpTill = min(len(self._gcode.layerList), self.layerSelect.getValue() + 1)
699 for n in xrange(0, drawUpTill):
700 c = 1.0 - float(drawUpTill - n) / 15
702 if len(self._gcodeVBOs) < n + 1:
703 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
704 if time.time() - t > 0.5:
707 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
709 self._gcodeVBOs[n][0].render(GL_LINES)
711 self._gcodeVBOs[n][1].render(GL_LINES)
712 glColor3f(c/2, c/2, 0.0)
713 self._gcodeVBOs[n][2].render(GL_LINES)
715 self._gcodeVBOs[n][3].render(GL_LINES)
716 self._gcodeVBOs[n][4].render(GL_LINES)
719 glStencilFunc(GL_ALWAYS, 1, 1)
720 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
721 self._objectShader.bind()
722 for obj in self._scene.objects():
723 if obj._loadAnim is not None:
724 if obj._loadAnim.isDone():
729 glDisable(GL_STENCIL_TEST)
730 if self._selectedObj == obj:
731 glEnable(GL_STENCIL_TEST)
732 if self._focusObj == obj:
734 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
736 if not self._scene.checkPlatform(obj):
737 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
738 self._renderObject(obj)
740 self._renderObject(obj, brightness)
741 self._objectShader.unbind()
743 glDisable(GL_STENCIL_TEST)
745 self._objectLoadShader.bind()
746 glColor4f(0.2, 0.6, 1.0, 1.0)
747 for obj in self._scene.objects():
748 if obj._loadAnim is None:
750 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
751 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
752 self._renderObject(obj)
753 self._objectLoadShader.unbind()
758 if self.viewMode == 'gcode':
761 #Draw the object box-shadow, so you can see where it will collide with other objects.
762 if self._selectedObj is not None and len(self._scene.objects()) > 1:
763 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
765 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0.0)
767 glEnable(GL_CULL_FACE)
768 glColor4f(0,0,0,0.12)
770 glVertex3f(-size[0], size[1], 0.1)
771 glVertex3f(-size[0], -size[1], 0.1)
772 glVertex3f( size[0], -size[1], 0.1)
773 glVertex3f( size[0], size[1], 0.1)
775 glDisable(GL_CULL_FACE)
778 #Draw the outline of the selected object, on top of everything else except the GUI.
779 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
780 glDisable(GL_DEPTH_TEST)
781 glEnable(GL_CULL_FACE)
782 glEnable(GL_STENCIL_TEST)
784 glStencilFunc(GL_EQUAL, 0, 255)
786 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
789 self._renderObject(self._selectedObj)
790 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
792 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
793 glDisable(GL_STENCIL_TEST)
794 glDisable(GL_CULL_FACE)
795 glEnable(GL_DEPTH_TEST)
797 if self._selectedObj is not None:
799 pos = self.getObjectCenterPos()
800 glTranslate(pos[0], pos[1], pos[2])
804 def _renderObject(self, obj, brightness = False):
806 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
808 if self.tempMatrix is not None and obj == self._selectedObj:
809 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
810 glMultMatrixf(tempMatrix)
812 offset = obj.getDrawOffset()
813 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
815 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
816 glMultMatrixf(tempMatrix)
819 for m in obj._meshList:
821 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
823 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
828 def _drawMachine(self):
829 glEnable(GL_CULL_FACE)
832 if profile.getPreference('machine_type') == 'ultimaker':
834 self._objectShader.bind()
835 self._renderObject(self._platformMesh)
836 self._objectShader.unbind()
838 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
839 v0 = [ size[0] / 2, size[1] / 2, size[2]]
840 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
841 v2 = [-size[0] / 2, size[1] / 2, size[2]]
842 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
843 v4 = [ size[0] / 2, size[1] / 2, 0]
844 v5 = [ size[0] / 2,-size[1] / 2, 0]
845 v6 = [-size[0] / 2, size[1] / 2, 0]
846 v7 = [-size[0] / 2,-size[1] / 2, 0]
848 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
849 glEnableClientState(GL_VERTEX_ARRAY)
850 glVertexPointer(3, GL_FLOAT, 3*4, vList)
852 glColor4ub(5, 171, 231, 64)
853 glDrawArrays(GL_QUADS, 0, 4)
854 glColor4ub(5, 171, 231, 96)
855 glDrawArrays(GL_QUADS, 4, 8)
856 glColor4ub(5, 171, 231, 128)
857 glDrawArrays(GL_QUADS, 12, 8)
859 sx = self._machineSize[0]
860 sy = self._machineSize[1]
861 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
862 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
867 x1 = max(min(x1, sx/2), -sx/2)
868 y1 = max(min(y1, sy/2), -sy/2)
869 x2 = max(min(x2, sx/2), -sx/2)
870 y2 = max(min(y2, sy/2), -sy/2)
871 if (x & 1) == (y & 1):
872 glColor4ub(5, 171, 231, 127)
874 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
876 glVertex3f(x1, y1, -0.02)
877 glVertex3f(x2, y1, -0.02)
878 glVertex3f(x2, y2, -0.02)
879 glVertex3f(x1, y2, -0.02)
882 glDisableClientState(GL_VERTEX_ARRAY)
884 glDisable(GL_CULL_FACE)
886 def _generateGCodeVBOs(self, layer):
888 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
889 pointList = numpy.zeros((0,3), numpy.float32)
891 if path.type == 'extrude' and path.pathType == extrudeType:
892 a = numpy.array(path.points, numpy.float32)
893 a = numpy.concatenate((a[:-1], a[1:]), 1)
894 a = a.reshape((len(a) * 2, 3))
895 pointList = numpy.concatenate((pointList, a))
896 ret.append(opengl.GLVBO(pointList))
899 def getObjectCenterPos(self):
900 if self._selectedObj is None:
901 return [0.0, 0.0, 0.0]
902 pos = self._selectedObj.getPosition()
903 size = self._selectedObj.getSize()
904 return [pos[0], pos[1], size[2]/2]
906 def getObjectBoundaryCircle(self):
907 if self._selectedObj is None:
909 return self._selectedObj.getBoundaryCircle()
911 def getObjectSize(self):
912 if self._selectedObj is None:
913 return [0.0, 0.0, 0.0]
914 return self._selectedObj.getSize()
916 def getObjectMatrix(self):
917 if self._selectedObj is None:
918 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
919 return self._selectedObj.getMatrix()
921 class shaderEditor(wx.Dialog):
922 def __init__(self, parent, callback, v, f):
923 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
924 self._callback = callback
925 s = wx.BoxSizer(wx.VERTICAL)
927 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
928 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
929 s.Add(self._vertex, 1, flag=wx.EXPAND)
930 s.Add(self._fragment, 1, flag=wx.EXPAND)
932 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
933 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
935 self.SetPosition(self.GetParent().GetPosition())
936 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
940 self._callback(self._vertex.GetValue(), self._fragment.GetValue())