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()
37 self._objectShader = None
39 self._selectedObj = None
40 self._objColors = [None,None,None,None]
43 self._mouseState = None
44 self._viewTarget = numpy.array([0,0,0], numpy.float32)
47 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
48 self._platformMesh._drawOffset = numpy.array([0,0,1.5], numpy.float32)
49 self._isSimpleMode = True
52 self._modelMatrix = None
53 self._projMatrix = None
54 self.tempMatrix = None
56 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.ShowLoadModel)
57 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.ShowPrintWindow)
58 self.printButton.setDisabled(True)
61 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
62 self.scaleToolButton = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
63 self.mirrorToolButton = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
65 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
66 self.layFlatButton = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
68 self.resetScaleButton = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
69 self.scaleMaxButton = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
71 self.mirrorXButton = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
72 self.mirrorYButton = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
73 self.mirrorZButton = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
75 self.rotateToolButton.setExpandArrow(True)
76 self.scaleToolButton.setExpandArrow(True)
77 self.mirrorToolButton.setExpandArrow(True)
79 self.scaleForm = openglGui.glFrame(self, (2, -2))
80 openglGui.glGuiLayoutGrid(self.scaleForm)
81 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
82 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
83 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
84 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
85 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
86 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
87 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
88 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
89 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
90 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
91 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
92 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
93 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
94 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
96 self.viewSelection = openglGui.glComboButton(self, 'View mode', [7,23], ['Normal', 'Layers'], (-1,0), self.OnViewChange)
97 self.layerSelect = openglGui.glSlider(self, 100, 0, 100, (-1,-2), lambda : self.QueueRefresh())
99 self.notification = openglGui.glNotification(self, (0, 0))
101 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
102 self._sceneUpdateTimer = wx.Timer(self)
103 self.Bind(wx.EVT_TIMER, lambda e : self._slicer.runSlicer(self._scene), self._sceneUpdateTimer)
104 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
108 self.updateToolButtons()
109 self.updateProfileToControls()
111 def ShowLoadModel(self, button):
113 dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
114 dlg.SetWildcard(meshLoader.wildcardFilter())
115 if dlg.ShowModal() != wx.ID_OK:
118 filename = dlg.GetPath()
120 if not(os.path.exists(filename)):
122 profile.putPreference('lastFile', filename)
123 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
124 self.loadScene([filename])
126 def ShowPrintWindow(self, button):
128 if machineCom.machineIsConnected():
129 printWindow.printFile(self._slicer.getGCodeFilename())
130 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
131 drives = removableStorage.getPossibleSDcardDrives()
136 filename = os.path.basename(profile.getPreference('lastFile'))
137 filename = filename[0:filename.rfind('.')] + '.gcode'
138 shutil.copy(self._slicer.getGCodeFilename(), drive[1] + filename)
139 self.notification.message("Saved as %s" % (drive[1] + filename))
141 defPath = profile.getPreference('lastFile')
142 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
143 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
144 dlg.SetFilename(defPath)
145 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
146 if dlg.ShowModal() != wx.ID_OK:
149 filename = dlg.GetPath()
152 shutil.copy(self._slicer.getGCodeFilename(), filename)
153 self.notification.message("Saved as %s" % (filename))
155 def OnToolSelect(self, button):
156 if self.rotateToolButton.getSelected():
157 self.tool = previewTools.toolRotate(self)
158 elif self.scaleToolButton.getSelected():
159 self.tool = previewTools.toolScale(self)
160 elif self.mirrorToolButton.getSelected():
161 self.tool = previewTools.toolNone(self)
163 self.tool = previewTools.toolNone(self)
164 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
165 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
166 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
167 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
168 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
169 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
170 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
171 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
173 def updateToolButtons(self):
174 if self._selectedObj is None:
178 self.rotateToolButton.setHidden(hidden)
179 self.scaleToolButton.setHidden(hidden)
180 self.mirrorToolButton.setHidden(hidden)
182 self.rotateToolButton.setSelected(False)
183 self.scaleToolButton.setSelected(False)
184 self.mirrorToolButton.setSelected(False)
187 def OnViewChange(self):
188 if self.viewSelection.getValue() == 1:
189 self.viewMode = 'gcode'
190 if self._gcode is not None:
191 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
192 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
193 self._selectObject(None)
195 self.viewMode = 'normal'
196 self.layerSelect.setHidden(self.viewMode != 'gcode')
197 self.openFileButton.setHidden(self.viewMode == 'gcode')
200 def OnRotateReset(self, button):
201 if self._selectedObj is None:
203 self._selectedObj.resetRotation()
204 self._scene.pushFree()
205 self._selectObject(self._selectedObj)
207 def OnLayFlat(self, button):
208 if self._selectedObj is None:
210 self._selectedObj.layFlat()
211 self._scene.pushFree()
212 self._selectObject(self._selectedObj)
214 def OnScaleReset(self, button):
215 if self._selectedObj is None:
217 self._selectedObj.resetScale()
219 def OnScaleMax(self, button):
220 if self._selectedObj is None:
222 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2)
223 self._scene.pushFree()
224 self._selectObject(self._selectedObj)
226 def OnMirror(self, axis):
227 if self._selectedObj is None:
229 self._selectedObj.mirror(axis)
232 def OnScaleEntry(self, value, axis):
233 if self._selectedObj is None:
239 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
240 self.updateProfileToControls()
241 self._scene.pushFree()
242 self._selectObject(self._selectedObj)
245 def OnScaleEntryMM(self, value, axis):
246 if self._selectedObj is None:
252 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
253 self.updateProfileToControls()
254 self._scene.pushFree()
255 self._selectObject(self._selectedObj)
258 def OnDeleteAll(self, e):
259 while len(self._scene.objects()) > 0:
260 self._deleteObject(self._scene.objects()[0])
261 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
263 def OnMultiply(self, e):
264 if self._focusObj is None:
267 dlg = wx.NumberEntryDialog(self, "How many copies need to be made?", "Copies", "Multiply", 1, 1, 100)
268 if dlg.ShowModal() != wx.ID_OK:
277 self._scene.add(newObj)
278 self._scene.centerAll()
279 if not self._scene.checkPlatform(newObj):
283 self._scene.remove(newObj)
284 self._scene.centerAll()
287 def OnSplitObject(self, e):
288 if self._focusObj is None:
290 self._scene.remove(self._focusObj)
291 for obj in self._focusObj.split():
293 self._scene.centerAll()
294 self._selectObject(None)
297 def OnMergeObjects(self, e):
298 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
300 self._scene.merge(self._selectedObj, self._focusObj)
303 def sceneUpdated(self):
304 self._sceneUpdateTimer.Start(1, True)
305 self._slicer.abortSlicer()
306 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
309 def _updateSliceProgress(self, progressValue, ready):
310 self.printButton.setDisabled(not ready)
311 self.printButton.setProgressBar(progressValue)
313 self._gcode = gcodeInterpreter.gcode()
314 self._gcode.load(self._slicer.getGCodeFilename())
319 def loadScene(self, fileList):
320 for filename in fileList:
322 objList = meshLoader.loadMeshes(filename)
324 traceback.print_exc()
327 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
329 self._scene.centerAll()
330 self._selectObject(obj)
333 def _deleteObject(self, obj):
334 if obj == self._selectedObj:
335 self._selectObject(None)
336 if obj == self._focusObj:
337 self._focusObj = None
338 self._scene.remove(obj)
339 for m in obj._meshList:
340 if m.vbo is not None and m.vbo.decRef():
341 self.glReleaseList.append(m.vbo)
342 if self._isSimpleMode:
343 self._scene.arrangeAll()
346 def _selectObject(self, obj, zoom = True):
347 if obj != self._selectedObj:
348 self._selectedObj = obj
349 self.updateProfileToControls()
350 self.updateToolButtons()
351 if zoom and obj is not None:
352 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
353 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
354 newZoom = obj.getBoundaryCircle() * 6
355 if newZoom > numpy.max(self._machineSize) * 3:
356 newZoom = numpy.max(self._machineSize) * 3
357 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
359 def updateProfileToControls(self):
360 oldSimpleMode = self._isSimpleMode
361 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
362 if self._isSimpleMode and not oldSimpleMode:
363 self._scene.arrangeAll()
365 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
366 self._objColors[0] = profile.getPreferenceColour('model_colour')
367 self._objColors[1] = profile.getPreferenceColour('model_colour2')
368 self._objColors[2] = profile.getPreferenceColour('model_colour3')
369 self._objColors[3] = profile.getPreferenceColour('model_colour4')
370 self._scene.setMachineSize(self._machineSize)
371 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
372 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'))
374 if self._selectedObj is not None:
375 scale = self._selectedObj.getScale()
376 size = self._selectedObj.getSize()
377 self.scaleXctrl.setValue(round(scale[0], 2))
378 self.scaleYctrl.setValue(round(scale[1], 2))
379 self.scaleZctrl.setValue(round(scale[2], 2))
380 self.scaleXmmctrl.setValue(round(size[0], 2))
381 self.scaleYmmctrl.setValue(round(size[1], 2))
382 self.scaleZmmctrl.setValue(round(size[2], 2))
384 def OnKeyChar(self, keyCode):
385 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
386 if self._selectedObj is not None:
387 self._deleteObject(self._selectedObj)
389 if keyCode == wx.WXK_UP:
390 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
392 elif keyCode == wx.WXK_DOWN:
393 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
395 elif keyCode == wx.WXK_PAGEUP:
396 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
398 elif keyCode == wx.WXK_PAGEDOWN:
399 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
402 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
403 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
405 def ShaderUpdate(self, v, f):
406 s = opengl.GLShader(v, f)
408 self._objectLoadShader.release()
409 self._objectLoadShader = s
410 for obj in self._scene.objects():
411 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
414 def OnMouseDown(self,e):
415 self._mouseX = e.GetX()
416 self._mouseY = e.GetY()
417 self._mouseClick3DPos = self._mouse3Dpos
418 self._mouseClickFocus = self._focusObj
420 self._mouseState = 'doubleClick'
422 self._mouseState = 'dragOrClick'
423 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
424 p0 -= self.getObjectCenterPos() - self._viewTarget
425 p1 -= self.getObjectCenterPos() - self._viewTarget
426 if self.tool.OnDragStart(p0, p1):
427 self._mouseState = 'tool'
428 if self._mouseState == 'dragOrClick':
429 if e.GetButton() == 1:
430 if self._focusObj is not None:
431 self._selectObject(self._focusObj, False)
434 def OnMouseUp(self, e):
435 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
437 if self._mouseState == 'dragOrClick':
438 if e.GetButton() == 1:
439 self._selectObject(self._focusObj)
440 if e.GetButton() == 3:
442 if self._focusObj is not None:
443 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
444 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
445 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
446 if self._selectedObj != self._focusObj and self._focusObj is not None:
447 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
448 if len(self._scene.objects()) > 0:
449 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
450 if menu.MenuItemCount > 0:
453 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
454 self._scene.pushFree()
456 elif self._mouseState == 'tool':
457 if self.tempMatrix is not None and self._selectedObj is not None:
458 self._selectedObj.applyMatrix(self.tempMatrix)
459 self._scene.pushFree()
460 self._selectObject(self._selectedObj)
461 self.tempMatrix = None
462 self.tool.OnDragEnd()
464 self._mouseState = None
466 def OnMouseMotion(self,e):
467 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
468 p0 -= self.getObjectCenterPos() - self._viewTarget
469 p1 -= self.getObjectCenterPos() - self._viewTarget
471 if e.Dragging() and self._mouseState is not None:
472 if self._mouseState == 'tool':
473 self.tool.OnDrag(p0, p1)
474 elif not e.LeftIsDown() and e.RightIsDown():
475 self._mouseState = 'drag'
476 self._yaw += e.GetX() - self._mouseX
477 self._pitch -= e.GetY() - self._mouseY
478 if self._pitch > 170:
482 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
483 self._mouseState = 'drag'
484 self._zoom += e.GetY() - self._mouseY
487 if self._zoom > numpy.max(self._machineSize) * 3:
488 self._zoom = numpy.max(self._machineSize) * 3
489 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus and not self._isSimpleMode:
490 self._mouseState = 'dragObject'
491 z = max(0, self._mouseClick3DPos[2])
492 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
493 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
498 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
499 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
500 diff = cursorZ1 - cursorZ0
501 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
502 if not e.Dragging() or self._mouseState != 'tool':
503 self.tool.OnMouseMove(p0, p1)
505 self._mouseX = e.GetX()
506 self._mouseY = e.GetY()
508 def OnMouseWheel(self, e):
509 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
510 delta = max(min(delta,4),-4)
511 self._zoom *= 1.0 - delta / 10.0
514 if self._zoom > numpy.max(self._machineSize) * 3:
515 self._zoom = numpy.max(self._machineSize) * 3
518 def getMouseRay(self, x, y):
519 if self._viewport is None:
520 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
521 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
522 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
523 p0 -= self._viewTarget
524 p1 -= self._viewTarget
527 def _init3DView(self):
528 # set viewing projection
529 size = self.GetSize()
530 glViewport(0, 0, size.GetWidth(), size.GetHeight())
533 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
535 glDisable(GL_RESCALE_NORMAL)
536 glDisable(GL_LIGHTING)
538 glEnable(GL_DEPTH_TEST)
539 glDisable(GL_CULL_FACE)
541 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
543 glClearColor(0.8, 0.8, 0.8, 1.0)
547 glMatrixMode(GL_PROJECTION)
549 aspect = float(size.GetWidth()) / float(size.GetHeight())
550 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
552 glMatrixMode(GL_MODELVIEW)
554 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
557 if machineCom.machineIsConnected():
558 self.printButton._imageID = 6
559 self.printButton._tooltip = 'Print'
560 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
561 self.printButton._imageID = 2
562 self.printButton._tooltip = 'Toolpath to SD'
564 self.printButton._imageID = 3
565 self.printButton._tooltip = 'Save toolpath'
567 if self._animView is not None:
568 self._viewTarget = self._animView.getPosition()
569 if self._animView.isDone():
570 self._animView = None
571 if self._animZoom is not None:
572 self._zoom = self._animZoom.getPosition()
573 if self._animZoom.isDone():
574 self._animZoom = None
575 if self._objectShader is None:
576 self._objectShader = opengl.GLShader("""
577 varying float light_amount;
581 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
582 gl_FrontColor = gl_Color;
584 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
588 varying float light_amount;
592 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
595 self._objectLoadShader = opengl.GLShader("""
596 uniform float intensity;
598 varying float light_amount;
602 vec4 tmp = gl_Vertex;
603 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
604 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
605 gl_Position = gl_ModelViewProjectionMatrix * tmp;
606 gl_FrontColor = gl_Color;
608 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
612 uniform float intensity;
613 varying float light_amount;
617 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
621 glTranslate(0,0,-self._zoom)
622 glRotate(-self._pitch, 1,0,0)
623 glRotate(self._yaw, 0,0,1)
624 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
626 self._viewport = glGetIntegerv(GL_VIEWPORT)
627 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
628 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
630 glClearColor(1,1,1,1)
631 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
633 if self.viewMode != 'gcode':
634 for n in xrange(0, len(self._scene.objects())):
635 obj = self._scene.objects()[n]
636 glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
637 self._renderObject(obj)
639 if self._mouseX > -1:
640 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
641 if n < len(self._scene.objects()):
642 self._focusObj = self._scene.objects()[n]
644 self._focusObj = None
645 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
646 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
647 self._mouse3Dpos -= self._viewTarget
650 glTranslate(0,0,-self._zoom)
651 glRotate(-self._pitch, 1,0,0)
652 glRotate(self._yaw, 0,0,1)
653 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
655 if self.viewMode == 'gcode':
656 if self._gcode is not None:
657 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
660 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
661 for n in xrange(0, self.layerSelect.getValue() + 1):
662 opengl.DrawGCodeLayer(self._gcode.layerList[n])
665 glStencilFunc(GL_ALWAYS, 1, 1)
666 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
667 self._objectShader.bind()
668 for obj in self._scene.objects():
669 if obj._loadAnim is not None:
670 if obj._loadAnim.isDone():
675 glDisable(GL_STENCIL_TEST)
676 if self._selectedObj == obj:
677 glEnable(GL_STENCIL_TEST)
678 if self._focusObj == obj:
680 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
682 if not self._scene.checkPlatform(obj):
683 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
684 self._renderObject(obj)
686 self._renderObject(obj, brightness)
687 self._objectShader.unbind()
689 glDisable(GL_STENCIL_TEST)
691 self._objectLoadShader.bind()
692 glColor4f(0.2, 0.6, 1.0, 1.0)
693 for obj in self._scene.objects():
694 if obj._loadAnim is None:
696 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
697 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
698 self._renderObject(obj)
699 self._objectLoadShader.unbind()
704 if self.viewMode == 'gcode':
707 #Draw the object box-shadow, so you can see where it will collide with other objects.
708 if self._selectedObj is not None and len(self._scene.objects()) > 1:
709 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
711 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0.0)
713 glEnable(GL_CULL_FACE)
714 glColor4f(0,0,0,0.12)
716 glVertex3f(-size[0], size[1], 0.1)
717 glVertex3f(-size[0], -size[1], 0.1)
718 glVertex3f( size[0], -size[1], 0.1)
719 glVertex3f( size[0], size[1], 0.1)
721 glDisable(GL_CULL_FACE)
724 #Draw the outline of the selected object, on top of everything else except the GUI.
725 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
726 glDisable(GL_DEPTH_TEST)
727 glEnable(GL_CULL_FACE)
728 glEnable(GL_STENCIL_TEST)
730 glStencilFunc(GL_EQUAL, 0, 255)
732 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
735 self._renderObject(self._selectedObj)
736 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
738 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
739 glDisable(GL_STENCIL_TEST)
740 glDisable(GL_CULL_FACE)
741 glEnable(GL_DEPTH_TEST)
743 if self._selectedObj is not None:
745 pos = self.getObjectCenterPos()
746 glTranslate(pos[0], pos[1], pos[2])
750 def _renderObject(self, obj, brightness = False):
752 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
754 if self.tempMatrix is not None and obj == self._selectedObj:
755 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
756 glMultMatrixf(tempMatrix)
758 offset = obj.getDrawOffset()
759 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
761 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
762 glMultMatrixf(tempMatrix)
765 for m in obj._meshList:
767 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
769 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
774 def _drawMachine(self):
775 glEnable(GL_CULL_FACE)
778 if profile.getPreference('machine_type') == 'ultimaker':
780 self._objectShader.bind()
781 self._renderObject(self._platformMesh)
782 self._objectShader.unbind()
784 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
785 v0 = [ size[0] / 2, size[1] / 2, size[2]]
786 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
787 v2 = [-size[0] / 2, size[1] / 2, size[2]]
788 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
789 v4 = [ size[0] / 2, size[1] / 2, 0]
790 v5 = [ size[0] / 2,-size[1] / 2, 0]
791 v6 = [-size[0] / 2, size[1] / 2, 0]
792 v7 = [-size[0] / 2,-size[1] / 2, 0]
794 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
795 glEnableClientState(GL_VERTEX_ARRAY)
796 glVertexPointer(3, GL_FLOAT, 3*4, vList)
798 glColor4ub(5, 171, 231, 64)
799 glDrawArrays(GL_QUADS, 0, 4)
800 glColor4ub(5, 171, 231, 96)
801 glDrawArrays(GL_QUADS, 4, 8)
802 glColor4ub(5, 171, 231, 128)
803 glDrawArrays(GL_QUADS, 12, 8)
805 sx = self._machineSize[0]
806 sy = self._machineSize[1]
807 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
808 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
813 x1 = max(min(x1, sx/2), -sx/2)
814 y1 = max(min(y1, sy/2), -sy/2)
815 x2 = max(min(x2, sx/2), -sx/2)
816 y2 = max(min(y2, sy/2), -sy/2)
817 if (x & 1) == (y & 1):
818 glColor4ub(5, 171, 231, 127)
820 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
822 glVertex3f(x1, y1, -0.02)
823 glVertex3f(x2, y1, -0.02)
824 glVertex3f(x2, y2, -0.02)
825 glVertex3f(x1, y2, -0.02)
828 glDisableClientState(GL_VERTEX_ARRAY)
830 glDisable(GL_CULL_FACE)
832 def getObjectCenterPos(self):
833 if self._selectedObj is None:
834 return [0.0, 0.0, 0.0]
835 pos = self._selectedObj.getPosition()
836 size = self._selectedObj.getSize()
837 return [pos[0], pos[1], size[2]/2]
839 def getObjectBoundaryCircle(self):
840 if self._selectedObj is None:
842 return self._selectedObj.getBoundaryCircle()
844 def getObjectSize(self):
845 if self._selectedObj is None:
846 return [0.0, 0.0, 0.0]
847 return self._selectedObj.getSize()
849 def getObjectMatrix(self):
850 if self._selectedObj is None:
851 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
852 return self._selectedObj.getMatrix()
854 class shaderEditor(wx.Dialog):
855 def __init__(self, parent, callback, v, f):
856 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
857 self._callback = callback
858 s = wx.BoxSizer(wx.VERTICAL)
860 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
861 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
862 s.Add(self._vertex, 1, flag=wx.EXPAND)
863 s.Add(self._fragment, 1, flag=wx.EXPAND)
865 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
866 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
868 self.SetPosition(self.GetParent().GetPosition())
869 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
873 self._callback(self._vertex.GetValue(), self._fragment.GetValue())