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'
139 shutil.copy(self._slicer.getGCodeFilename(), drive[1] + filename)
140 self.notification.message("Saved as %s" % (drive[1] + filename))
142 defPath = profile.getPreference('lastFile')
143 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
144 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
145 dlg.SetFilename(defPath)
146 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
147 if dlg.ShowModal() != wx.ID_OK:
150 filename = dlg.GetPath()
153 shutil.copy(self._slicer.getGCodeFilename(), filename)
154 self.notification.message("Saved as %s" % (filename))
156 def OnToolSelect(self, button):
157 if self.rotateToolButton.getSelected():
158 self.tool = previewTools.toolRotate(self)
159 elif self.scaleToolButton.getSelected():
160 self.tool = previewTools.toolScale(self)
161 elif self.mirrorToolButton.getSelected():
162 self.tool = previewTools.toolNone(self)
164 self.tool = previewTools.toolNone(self)
165 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
166 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
167 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
168 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
169 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
170 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
171 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
172 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
174 def updateToolButtons(self):
175 if self._selectedObj is None:
179 self.rotateToolButton.setHidden(hidden)
180 self.scaleToolButton.setHidden(hidden)
181 self.mirrorToolButton.setHidden(hidden)
183 self.rotateToolButton.setSelected(False)
184 self.scaleToolButton.setSelected(False)
185 self.mirrorToolButton.setSelected(False)
188 def OnViewChange(self):
189 if self.viewSelection.getValue() == 1:
190 self.viewMode = 'gcode'
191 if self._gcode is not None:
192 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
193 self.layerSelect.setValue(len(self._gcode.layerList) - 1)
194 self._selectObject(None)
196 self.viewMode = 'normal'
197 self.layerSelect.setHidden(self.viewMode != 'gcode')
198 self.openFileButton.setHidden(self.viewMode == 'gcode')
201 def OnRotateReset(self, button):
202 if self._selectedObj is None:
204 self._selectedObj.resetRotation()
205 self._scene.pushFree()
206 self._selectObject(self._selectedObj)
208 def OnLayFlat(self, button):
209 if self._selectedObj is None:
211 self._selectedObj.layFlat()
212 self._scene.pushFree()
213 self._selectObject(self._selectedObj)
215 def OnScaleReset(self, button):
216 if self._selectedObj is None:
218 self._selectedObj.resetScale()
220 def OnScaleMax(self, button):
221 if self._selectedObj is None:
223 self._selectedObj.scaleUpTo(self._machineSize - numpy.array(profile.calculateObjectSizeOffsets() + [0.0], numpy.float32) * 2)
224 self._scene.pushFree()
225 self._selectObject(self._selectedObj)
227 def OnMirror(self, axis):
228 if self._selectedObj is None:
230 self._selectedObj.mirror(axis)
233 def OnScaleEntry(self, value, axis):
234 if self._selectedObj is None:
240 self._selectedObj.setScale(value, axis, self.scaleUniform.getValue())
241 self.updateProfileToControls()
242 self._scene.pushFree()
243 self._selectObject(self._selectedObj)
246 def OnScaleEntryMM(self, value, axis):
247 if self._selectedObj is None:
253 self._selectedObj.setSize(value, axis, self.scaleUniform.getValue())
254 self.updateProfileToControls()
255 self._scene.pushFree()
256 self._selectObject(self._selectedObj)
259 def OnDeleteAll(self, e):
260 while len(self._scene.objects()) > 0:
261 self._deleteObject(self._scene.objects()[0])
262 self._animView = openglGui.animation(self, self._viewTarget.copy(), numpy.array([0,0,0], numpy.float32), 0.5)
264 def OnMultiply(self, e):
265 if self._focusObj is None:
268 dlg = wx.NumberEntryDialog(self, "How many copies need to be made?", "Copies", "Multiply", 1, 1, 100)
269 if dlg.ShowModal() != wx.ID_OK:
278 self._scene.add(newObj)
279 self._scene.centerAll()
280 if not self._scene.checkPlatform(newObj):
284 self._scene.remove(newObj)
285 self._scene.centerAll()
288 def OnSplitObject(self, e):
289 if self._focusObj is None:
291 self._scene.remove(self._focusObj)
292 for obj in self._focusObj.split():
294 self._scene.centerAll()
295 self._selectObject(None)
298 def OnMergeObjects(self, e):
299 if self._selectedObj is None or self._focusObj is None or self._selectedObj == self._focusObj:
301 self._scene.merge(self._selectedObj, self._focusObj)
304 def sceneUpdated(self):
305 self._sceneUpdateTimer.Start(1, True)
306 self._slicer.abortSlicer()
307 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
310 def _updateSliceProgress(self, progressValue, ready):
311 self.printButton.setDisabled(not ready)
312 self.printButton.setProgressBar(progressValue)
313 if self._gcode is not None:
315 for layerVBOlist in self._gcodeVBOs:
316 for vbo in layerVBOlist:
317 self.glReleaseList.append(vbo)
320 self._gcode = gcodeInterpreter.gcode()
321 self._gcode.load(self._slicer.getGCodeFilename())
324 def loadScene(self, fileList):
325 for filename in fileList:
327 objList = meshLoader.loadMeshes(filename)
329 traceback.print_exc()
332 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
334 self._scene.centerAll()
335 self._selectObject(obj)
338 def _deleteObject(self, obj):
339 if obj == self._selectedObj:
340 self._selectObject(None)
341 if obj == self._focusObj:
342 self._focusObj = None
343 self._scene.remove(obj)
344 for m in obj._meshList:
345 if m.vbo is not None and m.vbo.decRef():
346 self.glReleaseList.append(m.vbo)
347 if self._isSimpleMode:
348 self._scene.arrangeAll()
351 def _selectObject(self, obj, zoom = True):
352 if obj != self._selectedObj:
353 self._selectedObj = obj
354 self.updateProfileToControls()
355 self.updateToolButtons()
356 if zoom and obj is not None:
357 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
358 self._animView = openglGui.animation(self, self._viewTarget.copy(), newViewPos, 0.5)
359 newZoom = obj.getBoundaryCircle() * 6
360 if newZoom > numpy.max(self._machineSize) * 3:
361 newZoom = numpy.max(self._machineSize) * 3
362 self._animZoom = openglGui.animation(self, self._zoom, newZoom, 0.5)
364 def updateProfileToControls(self):
365 oldSimpleMode = self._isSimpleMode
366 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
367 if self._isSimpleMode and not oldSimpleMode:
368 self._scene.arrangeAll()
370 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
371 self._objColors[0] = profile.getPreferenceColour('model_colour')
372 self._objColors[1] = profile.getPreferenceColour('model_colour2')
373 self._objColors[2] = profile.getPreferenceColour('model_colour3')
374 self._objColors[3] = profile.getPreferenceColour('model_colour4')
375 self._scene.setMachineSize(self._machineSize)
376 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
377 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'))
379 if self._selectedObj is not None:
380 scale = self._selectedObj.getScale()
381 size = self._selectedObj.getSize()
382 self.scaleXctrl.setValue(round(scale[0], 2))
383 self.scaleYctrl.setValue(round(scale[1], 2))
384 self.scaleZctrl.setValue(round(scale[2], 2))
385 self.scaleXmmctrl.setValue(round(size[0], 2))
386 self.scaleYmmctrl.setValue(round(size[1], 2))
387 self.scaleZmmctrl.setValue(round(size[2], 2))
389 def OnKeyChar(self, keyCode):
390 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
391 if self._selectedObj is not None:
392 self._deleteObject(self._selectedObj)
394 if keyCode == wx.WXK_UP:
395 self.layerSelect.setValue(self.layerSelect.getValue() + 1)
397 elif keyCode == wx.WXK_DOWN:
398 self.layerSelect.setValue(self.layerSelect.getValue() - 1)
400 elif keyCode == wx.WXK_PAGEUP:
401 self.layerSelect.setValue(self.layerSelect.getValue() + 10)
403 elif keyCode == wx.WXK_PAGEDOWN:
404 self.layerSelect.setValue(self.layerSelect.getValue() - 10)
407 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
408 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
410 def ShaderUpdate(self, v, f):
411 s = opengl.GLShader(v, f)
413 self._objectLoadShader.release()
414 self._objectLoadShader = s
415 for obj in self._scene.objects():
416 obj._loadAnim = openglGui.animation(self, 1, 0, 1.5)
419 def OnMouseDown(self,e):
420 self._mouseX = e.GetX()
421 self._mouseY = e.GetY()
422 self._mouseClick3DPos = self._mouse3Dpos
423 self._mouseClickFocus = self._focusObj
425 self._mouseState = 'doubleClick'
427 self._mouseState = 'dragOrClick'
428 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
429 p0 -= self.getObjectCenterPos() - self._viewTarget
430 p1 -= self.getObjectCenterPos() - self._viewTarget
431 if self.tool.OnDragStart(p0, p1):
432 self._mouseState = 'tool'
433 if self._mouseState == 'dragOrClick':
434 if e.GetButton() == 1:
435 if self._focusObj is not None:
436 self._selectObject(self._focusObj, False)
439 def OnMouseUp(self, e):
440 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
442 if self._mouseState == 'dragOrClick':
443 if e.GetButton() == 1:
444 self._selectObject(self._focusObj)
445 if e.GetButton() == 3:
447 if self._focusObj is not None:
448 self.Bind(wx.EVT_MENU, lambda e: self._deleteObject(self._focusObj), menu.Append(-1, 'Delete'))
449 self.Bind(wx.EVT_MENU, self.OnMultiply, menu.Append(-1, 'Multiply'))
450 self.Bind(wx.EVT_MENU, self.OnSplitObject, menu.Append(-1, 'Split'))
451 if self._selectedObj != self._focusObj and self._focusObj is not None:
452 self.Bind(wx.EVT_MENU, self.OnMergeObjects, menu.Append(-1, 'Dual extrusion merge'))
453 if len(self._scene.objects()) > 0:
454 self.Bind(wx.EVT_MENU, self.OnDeleteAll, menu.Append(-1, 'Delete all'))
455 if menu.MenuItemCount > 0:
458 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
459 self._scene.pushFree()
461 elif self._mouseState == 'tool':
462 if self.tempMatrix is not None and self._selectedObj is not None:
463 self._selectedObj.applyMatrix(self.tempMatrix)
464 self._scene.pushFree()
465 self._selectObject(self._selectedObj)
466 self.tempMatrix = None
467 self.tool.OnDragEnd()
469 self._mouseState = None
471 def OnMouseMotion(self,e):
472 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
473 p0 -= self.getObjectCenterPos() - self._viewTarget
474 p1 -= self.getObjectCenterPos() - self._viewTarget
476 if e.Dragging() and self._mouseState is not None:
477 if self._mouseState == 'tool':
478 self.tool.OnDrag(p0, p1)
479 elif not e.LeftIsDown() and e.RightIsDown():
480 self._mouseState = 'drag'
481 self._yaw += e.GetX() - self._mouseX
482 self._pitch -= e.GetY() - self._mouseY
483 if self._pitch > 170:
487 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
488 self._mouseState = 'drag'
489 self._zoom += e.GetY() - self._mouseY
492 if self._zoom > numpy.max(self._machineSize) * 3:
493 self._zoom = numpy.max(self._machineSize) * 3
494 elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus and not self._isSimpleMode:
495 self._mouseState = 'dragObject'
496 z = max(0, self._mouseClick3DPos[2])
497 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
498 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
503 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
504 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
505 diff = cursorZ1 - cursorZ0
506 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
507 if not e.Dragging() or self._mouseState != 'tool':
508 self.tool.OnMouseMove(p0, p1)
510 self._mouseX = e.GetX()
511 self._mouseY = e.GetY()
513 def OnMouseWheel(self, e):
514 delta = float(e.GetWheelRotation()) / float(e.GetWheelDelta())
515 delta = max(min(delta,4),-4)
516 self._zoom *= 1.0 - delta / 10.0
519 if self._zoom > numpy.max(self._machineSize) * 3:
520 self._zoom = numpy.max(self._machineSize) * 3
523 def getMouseRay(self, x, y):
524 if self._viewport is None:
525 return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
526 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
527 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
528 p0 -= self._viewTarget
529 p1 -= self._viewTarget
532 def _init3DView(self):
533 # set viewing projection
534 size = self.GetSize()
535 glViewport(0, 0, size.GetWidth(), size.GetHeight())
538 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
540 glDisable(GL_RESCALE_NORMAL)
541 glDisable(GL_LIGHTING)
543 glEnable(GL_DEPTH_TEST)
544 glDisable(GL_CULL_FACE)
546 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
548 glClearColor(0.8, 0.8, 0.8, 1.0)
552 glMatrixMode(GL_PROJECTION)
554 aspect = float(size.GetWidth()) / float(size.GetHeight())
555 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
557 glMatrixMode(GL_MODELVIEW)
559 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
562 if machineCom.machineIsConnected():
563 self.printButton._imageID = 6
564 self.printButton._tooltip = 'Print'
565 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
566 self.printButton._imageID = 2
567 self.printButton._tooltip = 'Toolpath to SD'
569 self.printButton._imageID = 3
570 self.printButton._tooltip = 'Save toolpath'
572 if self._animView is not None:
573 self._viewTarget = self._animView.getPosition()
574 if self._animView.isDone():
575 self._animView = None
576 if self._animZoom is not None:
577 self._zoom = self._animZoom.getPosition()
578 if self._animZoom.isDone():
579 self._animZoom = None
580 if self._objectShader is None:
581 self._objectShader = opengl.GLShader("""
582 varying float light_amount;
586 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
587 gl_FrontColor = gl_Color;
589 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
593 varying float light_amount;
597 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
600 self._objectLoadShader = opengl.GLShader("""
601 uniform float intensity;
603 varying float light_amount;
607 vec4 tmp = gl_Vertex;
608 tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
609 tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
610 gl_Position = gl_ModelViewProjectionMatrix * tmp;
611 gl_FrontColor = gl_Color;
613 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
617 uniform float intensity;
618 varying float light_amount;
622 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
626 glTranslate(0,0,-self._zoom)
627 glRotate(-self._pitch, 1,0,0)
628 glRotate(self._yaw, 0,0,1)
629 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
631 self._viewport = glGetIntegerv(GL_VIEWPORT)
632 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
633 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
635 glClearColor(1,1,1,1)
636 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
638 if self.viewMode != 'gcode':
639 for n in xrange(0, len(self._scene.objects())):
640 obj = self._scene.objects()[n]
641 glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
642 self._renderObject(obj)
644 if self._mouseX > -1:
645 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
646 if n < len(self._scene.objects()):
647 self._focusObj = self._scene.objects()[n]
649 self._focusObj = None
650 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
651 self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
652 self._mouse3Dpos -= self._viewTarget
655 glTranslate(0,0,-self._zoom)
656 glRotate(-self._pitch, 1,0,0)
657 glRotate(self._yaw, 0,0,1)
658 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
660 if self.viewMode == 'gcode':
661 if self._gcode is not None:
662 self.layerSelect.setRange(1, len(self._gcode.layerList) - 1)
665 glTranslate(-self._machineSize[0] / 2, -self._machineSize[1] / 2, 0)
667 for n in xrange(0, self.layerSelect.getValue() + 1):
668 if len(self._gcodeVBOs) < n + 1:
669 self._gcodeVBOs.append(self._generateGCodeVBOs(self._gcode.layerList[n]))
670 if time.time() - t > 0.5:
673 #['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']
675 self._gcodeVBOs[n][0].render(GL_LINES)
677 self._gcodeVBOs[n][1].render(GL_LINES)
678 glColor3f(0.5, 0.5, 0.0)
679 self._gcodeVBOs[n][2].render(GL_LINES)
681 self._gcodeVBOs[n][3].render(GL_LINES)
682 self._gcodeVBOs[n][4].render(GL_LINES)
685 glStencilFunc(GL_ALWAYS, 1, 1)
686 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
687 self._objectShader.bind()
688 for obj in self._scene.objects():
689 if obj._loadAnim is not None:
690 if obj._loadAnim.isDone():
695 glDisable(GL_STENCIL_TEST)
696 if self._selectedObj == obj:
697 glEnable(GL_STENCIL_TEST)
698 if self._focusObj == obj:
700 elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
702 if not self._scene.checkPlatform(obj):
703 glColor4f(0.5 * brightness, 0.5 * brightness, 0.5 * brightness, 0.8 * brightness)
704 self._renderObject(obj)
706 self._renderObject(obj, brightness)
707 self._objectShader.unbind()
709 glDisable(GL_STENCIL_TEST)
711 self._objectLoadShader.bind()
712 glColor4f(0.2, 0.6, 1.0, 1.0)
713 for obj in self._scene.objects():
714 if obj._loadAnim is None:
716 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
717 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
718 self._renderObject(obj)
719 self._objectLoadShader.unbind()
724 if self.viewMode == 'gcode':
727 #Draw the object box-shadow, so you can see where it will collide with other objects.
728 if self._selectedObj is not None and len(self._scene.objects()) > 1:
729 size = self._selectedObj.getSize()[0:2] / 2 + self._scene.getObjectExtend()
731 glTranslatef(self._selectedObj.getPosition()[0], self._selectedObj.getPosition()[1], 0.0)
733 glEnable(GL_CULL_FACE)
734 glColor4f(0,0,0,0.12)
736 glVertex3f(-size[0], size[1], 0.1)
737 glVertex3f(-size[0], -size[1], 0.1)
738 glVertex3f( size[0], -size[1], 0.1)
739 glVertex3f( size[0], size[1], 0.1)
741 glDisable(GL_CULL_FACE)
744 #Draw the outline of the selected object, on top of everything else except the GUI.
745 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
746 glDisable(GL_DEPTH_TEST)
747 glEnable(GL_CULL_FACE)
748 glEnable(GL_STENCIL_TEST)
750 glStencilFunc(GL_EQUAL, 0, 255)
752 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
755 self._renderObject(self._selectedObj)
756 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
758 glViewport(0, 0, self.GetSize().GetWidth(), self.GetSize().GetHeight())
759 glDisable(GL_STENCIL_TEST)
760 glDisable(GL_CULL_FACE)
761 glEnable(GL_DEPTH_TEST)
763 if self._selectedObj is not None:
765 pos = self.getObjectCenterPos()
766 glTranslate(pos[0], pos[1], pos[2])
770 def _renderObject(self, obj, brightness = False):
772 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
774 if self.tempMatrix is not None and obj == self._selectedObj:
775 tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
776 glMultMatrixf(tempMatrix)
778 offset = obj.getDrawOffset()
779 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
781 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
782 glMultMatrixf(tempMatrix)
785 for m in obj._meshList:
787 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
789 glColor4fv(map(lambda n: n * brightness, self._objColors[n]))
794 def _drawMachine(self):
795 glEnable(GL_CULL_FACE)
798 if profile.getPreference('machine_type') == 'ultimaker':
800 self._objectShader.bind()
801 self._renderObject(self._platformMesh)
802 self._objectShader.unbind()
804 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
805 v0 = [ size[0] / 2, size[1] / 2, size[2]]
806 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
807 v2 = [-size[0] / 2, size[1] / 2, size[2]]
808 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
809 v4 = [ size[0] / 2, size[1] / 2, 0]
810 v5 = [ size[0] / 2,-size[1] / 2, 0]
811 v6 = [-size[0] / 2, size[1] / 2, 0]
812 v7 = [-size[0] / 2,-size[1] / 2, 0]
814 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
815 glEnableClientState(GL_VERTEX_ARRAY)
816 glVertexPointer(3, GL_FLOAT, 3*4, vList)
818 glColor4ub(5, 171, 231, 64)
819 glDrawArrays(GL_QUADS, 0, 4)
820 glColor4ub(5, 171, 231, 96)
821 glDrawArrays(GL_QUADS, 4, 8)
822 glColor4ub(5, 171, 231, 128)
823 glDrawArrays(GL_QUADS, 12, 8)
825 sx = self._machineSize[0]
826 sy = self._machineSize[1]
827 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
828 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
833 x1 = max(min(x1, sx/2), -sx/2)
834 y1 = max(min(y1, sy/2), -sy/2)
835 x2 = max(min(x2, sx/2), -sx/2)
836 y2 = max(min(y2, sy/2), -sy/2)
837 if (x & 1) == (y & 1):
838 glColor4ub(5, 171, 231, 127)
840 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
842 glVertex3f(x1, y1, -0.02)
843 glVertex3f(x2, y1, -0.02)
844 glVertex3f(x2, y2, -0.02)
845 glVertex3f(x1, y2, -0.02)
848 glDisableClientState(GL_VERTEX_ARRAY)
850 glDisable(GL_CULL_FACE)
852 def _generateGCodeVBOs(self, layer):
854 for extrudeType in ['WALL-OUTER', 'WALL-INNER', 'FILL', 'SUPPORT', 'SKIRT']:
855 pointList = numpy.zeros((0,3), numpy.float32)
857 if path.type == 'extrude' and path.pathType == extrudeType:
858 a = numpy.array(path.points, numpy.float32)
859 a = numpy.concatenate((a[:-1], a[1:]), 1)
860 a = a.reshape((len(a) * 2, 3))
861 pointList = numpy.concatenate((pointList, a))
862 ret.append(opengl.GLVBO(pointList))
865 def getObjectCenterPos(self):
866 if self._selectedObj is None:
867 return [0.0, 0.0, 0.0]
868 pos = self._selectedObj.getPosition()
869 size = self._selectedObj.getSize()
870 return [pos[0], pos[1], size[2]/2]
872 def getObjectBoundaryCircle(self):
873 if self._selectedObj is None:
875 return self._selectedObj.getBoundaryCircle()
877 def getObjectSize(self):
878 if self._selectedObj is None:
879 return [0.0, 0.0, 0.0]
880 return self._selectedObj.getSize()
882 def getObjectMatrix(self):
883 if self._selectedObj is None:
884 return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
885 return self._selectedObj.getMatrix()
887 class shaderEditor(wx.Dialog):
888 def __init__(self, parent, callback, v, f):
889 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
890 self._callback = callback
891 s = wx.BoxSizer(wx.VERTICAL)
893 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
894 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
895 s.Add(self._vertex, 1, flag=wx.EXPAND)
896 s.Add(self._fragment, 1, flag=wx.EXPAND)
898 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
899 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
901 self.SetPosition(self.GetParent().GetPosition())
902 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
906 self._callback(self._vertex.GetValue(), self._fragment.GetValue())