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.gui.util import opengl
24 from Cura.gui.util import openglGui
27 def __init__(self, start, end, runTime):
30 self._startTime = time.time()
31 self._runTime = runTime
34 return time.time() > self._startTime + self._runTime
36 def getPosition(self):
39 f = (time.time() - self._startTime) / self._runTime
42 #f = 6*tc*ts + -15*ts*ts + 10*tc
44 return self._start + (self._end - self._start) * f
46 class SceneView(openglGui.glGuiPanel):
47 def __init__(self, parent):
48 super(SceneView, self).__init__(parent)
53 self._scene = objectScene.Scene()
54 self._objectShader = None
56 self._selectedObj = None
57 self._objColors = [None,None,None,None]
60 self._mouseState = None
61 self._viewTarget = numpy.array([0,0,0], numpy.float32)
64 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
65 self._platformMesh._drawOffset = numpy.array([0,0,0.5], numpy.float32)
66 self._isSimpleMode = True
68 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.ShowLoadModel)
69 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.ShowPrintWindow)
70 self.printButton.setDisabled(True)
72 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
73 self._sceneUpdateTimer = wx.Timer(self)
74 self.Bind(wx.EVT_TIMER, lambda e : self._slicer.runSlicer(self._scene), self._sceneUpdateTimer)
76 self.updateProfileToControls()
77 wx.EVT_IDLE(self, self.OnIdle)
79 def ShowLoadModel(self, button):
81 dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
82 dlg.SetWildcard(meshLoader.wildcardFilter())
83 if dlg.ShowModal() != wx.ID_OK:
86 filename = dlg.GetPath()
88 if not(os.path.exists(filename)):
90 profile.putPreference('lastFile', filename)
91 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
92 self.loadScene([filename])
94 def ShowPrintWindow(self, button):
96 if machineCom.machineIsConnected():
97 printWindow.printFile(self._slicer.getGCodeFilename())
98 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
99 drives = removableStorage.getPossibleSDcardDrives()
103 defPath = profile.getPreference('lastFile')
104 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
105 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
106 dlg.SetFilename(defPath)
107 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
108 if dlg.ShowModal() != wx.ID_OK:
111 filename = dlg.GetPath()
114 shutil.copy(self._slicer.getGCodeFilename(), filename)
117 if self._animView is not None or self._animZoom is not None:
120 for obj in self._scene.objects():
121 if obj._loadAnim is not None:
125 def sceneUpdated(self):
126 self._sceneUpdateTimer.Start(1, True)
127 self._slicer.abortSlicer()
130 def _updateSliceProgress(self, progressValue, ready):
131 self.printButton.setDisabled(not ready)
132 self.printButton.setProgressBar(progressValue)
135 def loadScene(self, fileList):
136 for filename in fileList:
138 objList = meshLoader.loadMeshes(filename)
140 traceback.print_exc()
143 obj._loadAnim = anim(1, 0, 1.5)
145 self._scene.centerAll()
146 self._selectObject(obj)
149 def _deleteObject(self, obj):
150 if obj == self._selectedObj:
151 self._selectedObj = None
152 if obj == self._focusObj:
153 self._focusObj = None
154 self._scene.remove(obj)
155 for m in obj._meshList:
156 if m.vbo is not None:
157 self.glReleaseList.append(m.vbo)
158 if self._isSimpleMode:
159 self._scene.arrangeAll()
162 def _selectObject(self, obj, zoom = True):
163 if obj != self._selectedObj:
164 self._selectedObj = obj
166 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
167 self._animView = anim(self._viewTarget.copy(), newViewPos, 0.5)
168 newZoom = obj.getBoundaryCircle() * 6
169 if newZoom > numpy.max(self._machineSize) * 3:
170 newZoom = numpy.max(self._machineSize) * 3
171 self._animZoom = anim(self._zoom, newZoom, 0.5)
173 def updateProfileToControls(self):
174 oldSimpleMode = self._isSimpleMode
175 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
176 if self._isSimpleMode and not oldSimpleMode:
177 self._scene.arrangeAll()
179 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
180 self._objColors[0] = profile.getPreferenceColour('model_colour')
181 self._objColors[1] = profile.getPreferenceColour('model_colour2')
182 self._objColors[2] = profile.getPreferenceColour('model_colour3')
183 self._objColors[3] = profile.getPreferenceColour('model_colour4')
184 self._scene.setMachineSize(self._machineSize)
186 def OnKeyChar(self, keyCode):
187 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
188 if self._selectedObj is not None:
189 self._deleteObject(self._selectedObj)
192 if keyCode == wx.WXK_F3:
193 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
195 def ShaderUpdate(self, v, f):
196 s = opengl.GLShader(v, f)
198 self._objectLoadShader.release()
199 self._objectLoadShader = s
200 for obj in self._scene.objects():
201 obj._loadAnim = anim(1, 0, 1.5)
204 def OnMouseDown(self,e):
205 self._mouseX = e.GetX()
206 self._mouseY = e.GetY()
207 self._mouseClick3DPos = self._mouse3Dpos
209 self._mouseState = 'doubleClick'
211 self._mouseState = 'dragOrClick'
212 if self._mouseState == 'dragOrClick':
214 if self._focusObj is not None:
215 self._selectObject(self._focusObj, False)
218 def OnMouseUp(self, e):
219 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
221 if self._mouseState == 'dragOrClick':
223 if self._focusObj is not None:
224 self._selectObject(self._focusObj)
226 self._selectedObj = None
228 if e.Button == 3 and self._selectedObj == self._focusObj:
230 #menu.Append(-1, 'Test')
231 #self.PopupMenu(menu)
234 if self._mouseState == 'dragObject' and self._selectedObj is not None:
235 self._scene.pushFree()
237 self._mouseState = None
239 def OnMouseMotion(self,e):
240 if e.Dragging() and self._mouseState is not None:
241 self._mouseState = 'drag'
242 if not e.LeftIsDown() and e.RightIsDown():
243 self._yaw += e.GetX() - self._mouseX
244 self._pitch -= e.GetY() - self._mouseY
245 if self._pitch > 170:
249 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
250 self._zoom += e.GetY() - self._mouseY
253 if self._zoom > numpy.max(self._machineSize) * 3:
254 self._zoom = numpy.max(self._machineSize) * 3
255 elif e.LeftIsDown() and self._selectedObj is not None and not self._isSimpleMode:
256 self._mouseState = 'dragObject'
257 z = max(0, self._mouseClick3DPos[2])
258 p0 = opengl.unproject(self._mouseX, self.viewport[1] + self.viewport[3] - self._mouseY, 0, self.modelMatrix, self.projMatrix, self.viewport)
259 p1 = opengl.unproject(self._mouseX, self.viewport[1] + self.viewport[3] - self._mouseY, 1, self.modelMatrix, self.projMatrix, self.viewport)
260 p2 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport)
261 p3 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport)
266 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
267 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
268 diff = cursorZ1 - cursorZ0
269 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
271 self._mouseX = e.GetX()
272 self._mouseY = e.GetY()
274 def _init3DView(self):
275 # set viewing projection
276 size = self.GetSize()
277 glViewport(0, 0, size.GetWidth(), size.GetHeight())
280 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
282 glDisable(GL_RESCALE_NORMAL)
283 glDisable(GL_LIGHTING)
285 glEnable(GL_DEPTH_TEST)
286 glDisable(GL_CULL_FACE)
288 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
290 glClearColor(0.8, 0.8, 0.8, 1.0)
294 glMatrixMode(GL_PROJECTION)
296 aspect = float(size.GetWidth()) / float(size.GetHeight())
297 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
299 glMatrixMode(GL_MODELVIEW)
301 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
304 if machineCom.machineIsConnected():
305 self.printButton._imageID = 6
306 self.printButton._tooltip = 'Print'
307 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
308 self.printButton._imageID = 2
309 self.printButton._tooltip = 'Toolpath to SD'
311 self.printButton._imageID = 3
312 self.printButton._tooltip = 'Save toolpath'
314 if self._animView is not None:
315 self._viewTarget = self._animView.getPosition()
316 if self._animView.isDone():
317 self._animView = None
318 if self._animZoom is not None:
319 self._zoom = self._animZoom.getPosition()
320 if self._animZoom.isDone():
321 self._animZoom = None
322 if self._objectShader is None:
323 self._objectShader = opengl.GLShader("""
324 uniform float cameraDistance;
325 varying float light_amount;
329 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
330 gl_FrontColor = gl_Color;
332 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
333 light_amount *= 1 - (length(gl_Position.xyz - vec3(0,0,cameraDistance)) / 1.5 / cameraDistance);
337 uniform float cameraDistance;
338 varying float light_amount;
342 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
345 self._objectLoadShader = opengl.GLShader("""
346 uniform float cameraDistance;
347 uniform float intensity;
349 varying float light_amount;
353 vec4 tmp = gl_Vertex;
354 tmp.x += sin(tmp.z/5+intensity*30) * scale * intensity;
355 tmp.y += sin(tmp.z/3+intensity*40) * scale * intensity;
356 gl_Position = gl_ModelViewProjectionMatrix * tmp;
357 gl_FrontColor = gl_Color;
359 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
360 light_amount *= 1 - (length(gl_Position.xyz - vec3(0,0,cameraDistance)) / 1.5 / cameraDistance);
364 uniform float cameraDistance;
365 uniform float intensity;
366 varying float light_amount;
370 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1-intensity);
374 glTranslate(0,0,-self._zoom)
375 glRotate(-self._pitch, 1,0,0)
376 glRotate(self._yaw, 0,0,1)
377 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
379 self.viewport = glGetIntegerv(GL_VIEWPORT)
380 self.modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
381 self.projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
383 glClearColor(1,1,1,1)
384 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
386 for n in xrange(0, len(self._scene.objects())):
387 obj = self._scene.objects()[n]
388 glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
389 self._renderObject(obj)
391 if self._mouseX > -1:
392 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
393 if n < len(self._scene.objects()):
394 self._focusObj = self._scene.objects()[n]
396 self._focusObj = None
397 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
398 self._mouse3Dpos = opengl.unproject(self._mouseX, self.viewport[1] + self.viewport[3] - self._mouseY, f, self.modelMatrix, self.projMatrix, self.viewport)
401 glTranslate(0,0,-self._zoom)
402 glRotate(-self._pitch, 1,0,0)
403 glRotate(self._yaw, 0,0,1)
404 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
406 glStencilFunc(GL_ALWAYS, 1, 1)
407 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
408 self._objectShader.bind()
409 self._objectShader.setUniform('cameraDistance', self._zoom)
410 for obj in self._scene.objects():
411 if obj._loadAnim is not None:
412 if obj._loadAnim.isDone():
416 col = self._objColors[0]
417 if not self._scene.checkPlatform(obj):
418 col = [0.5,0.5,0.5,0.8]
419 glDisable(GL_STENCIL_TEST)
420 if self._selectedObj == obj:
421 col = map(lambda n: n * 1.5, col)
422 glEnable(GL_STENCIL_TEST)
423 elif self._focusObj == obj:
424 col = map(lambda n: n * 1.2, col)
425 elif self._focusObj is not None or self._selectedObj is not None:
426 col = map(lambda n: n * 0.8, col)
427 glColor4f(col[0], col[1], col[2], col[3])
428 self._renderObject(obj)
429 self._objectShader.unbind()
431 glDisable(GL_STENCIL_TEST)
433 self._objectLoadShader.bind()
434 self._objectLoadShader.setUniform('cameraDistance', self._zoom)
435 glColor4f(0.2, 0.6, 1.0, 1.0)
436 for obj in self._scene.objects():
437 if obj._loadAnim is None:
439 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
440 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
441 self._renderObject(obj)
442 self._objectLoadShader.unbind()
447 #Draw the outline of the selected object, on top of everything else except the GUI.
448 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
449 glDisable(GL_DEPTH_TEST)
450 glEnable(GL_CULL_FACE)
451 glEnable(GL_STENCIL_TEST)
452 glStencilFunc(GL_EQUAL, 0, 255)
453 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
456 self._renderObject(self._selectedObj)
457 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
458 glDisable(GL_STENCIL_TEST)
459 glDisable(GL_CULL_FACE)
460 glEnable(GL_DEPTH_TEST)
462 def _renderObject(self, obj):
464 glTranslate(obj.getPosition()[0], obj.getPosition()[1], 0)
465 offset = obj.getDrawOffset()
466 glTranslate(-offset[0], -offset[1], -offset[2])
467 for m in obj._meshList:
469 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
473 def _drawMachine(self):
474 glEnable(GL_CULL_FACE)
477 if profile.getPreference('machine_type') == 'ultimaker':
479 self._objectShader.bind()
480 self._objectShader.setUniform('cameraDistance', self._zoom)
481 self._renderObject(self._platformMesh)
482 self._objectShader.unbind()
484 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
485 v0 = [ size[0] / 2, size[1] / 2, size[2]]
486 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
487 v2 = [-size[0] / 2, size[1] / 2, size[2]]
488 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
489 v4 = [ size[0] / 2, size[1] / 2, 0]
490 v5 = [ size[0] / 2,-size[1] / 2, 0]
491 v6 = [-size[0] / 2, size[1] / 2, 0]
492 v7 = [-size[0] / 2,-size[1] / 2, 0]
494 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
495 glEnableClientState(GL_VERTEX_ARRAY)
496 glVertexPointer(3, GL_FLOAT, 3*4, vList)
498 glColor4ub(5, 171, 231, 64)
499 glDrawArrays(GL_QUADS, 0, 4)
500 glColor4ub(5, 171, 231, 96)
501 glDrawArrays(GL_QUADS, 4, 8)
502 glColor4ub(5, 171, 231, 128)
503 glDrawArrays(GL_QUADS, 12, 8)
505 sx = self._machineSize[0]
506 sy = self._machineSize[1]
507 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
508 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
513 x1 = max(min(x1, sx/2), -sx/2)
514 y1 = max(min(y1, sy/2), -sy/2)
515 x2 = max(min(x2, sx/2), -sx/2)
516 y2 = max(min(y2, sy/2), -sy/2)
517 if (x & 1) == (y & 1):
518 glColor4ub(5, 171, 231, 127)
520 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
522 glVertex3f(x1, y1, -0.02)
523 glVertex3f(x2, y1, -0.02)
524 glVertex3f(x2, y2, -0.02)
525 glVertex3f(x1, y2, -0.02)
528 glDisableClientState(GL_VERTEX_ARRAY)
530 glDisable(GL_CULL_FACE)
532 class shaderEditor(wx.Dialog):
533 def __init__(self, parent, callback, v, f):
534 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
535 self._callback = callback
536 s = wx.BoxSizer(wx.VERTICAL)
538 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
539 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
540 s.Add(self._vertex, 1, flag=wx.EXPAND)
541 s.Add(self._fragment, 1, flag=wx.EXPAND)
543 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
544 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
546 self.SetPosition(self.GetParent().GetPosition())
547 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
551 self._callback(self._vertex.GetValue(), self._fragment.GetValue())