1 from __future__ import absolute_import
9 OpenGL.ERROR_CHECKING = False
10 from OpenGL.GLU import *
11 from OpenGL.GL import *
13 from Cura.util import profile
14 from Cura.util import meshLoader
15 from Cura.util import objectScene
16 from Cura.util import resources
17 from Cura.util import sliceEngine
18 from Cura.gui.util import opengl
19 from Cura.gui.util import openglGui
22 def __init__(self, start, end, runTime):
25 self._startTime = time.time()
26 self._runTime = runTime
29 return time.time() > self._startTime + self._runTime
31 def getPosition(self):
34 f = (time.time() - self._startTime) / self._runTime
37 #f = 6*tc*ts + -15*ts*ts + 10*tc
39 return self._start + (self._end - self._start) * f
41 class SceneView(openglGui.glGuiPanel):
42 def __init__(self, parent):
43 super(SceneView, self).__init__(parent)
48 self._scene = objectScene.Scene()
49 self._objectShader = None
51 self._selectedObj = None
52 self._objColors = [None,None,None,None]
55 self._mouseState = None
56 self._viewTarget = numpy.array([0,0,0], numpy.float32)
59 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
60 self._platformMesh._drawOffset = numpy.array([0,0,0.5], numpy.float32)
61 self._isSimpleMode = True
62 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
63 self._sceneUpdateTimer = wx.Timer(self)
64 self.Bind(wx.EVT_TIMER, lambda e : self._slicer.runSlicer(self._scene), self._sceneUpdateTimer)
66 self.openFileButton = openglGui.glButton(self, 4, 'Load', (0,0), self.ShowLoadModel)
67 self.printButton = openglGui.glButton(self, 6, 'Print', (1,0), self.ShowPrintWindow)
68 self.printButton.setDisabled(True)
70 self.updateProfileToControls()
71 wx.EVT_IDLE(self, self.OnIdle)
73 def ShowLoadModel(self):
74 dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
75 dlg.SetWildcard(meshLoader.wildcardFilter())
76 if dlg.ShowModal() != wx.ID_OK:
79 filename = dlg.GetPath()
81 if not(os.path.exists(filename)):
83 profile.putPreference('lastFile', filename)
84 self.GetParent().GetParent().GetParent().addToModelMRU(filename)
85 self.loadScene([filename])
87 def ShowPrintWindow(self):
91 if self._animView is not None or self._animZoom is not None:
94 for obj in self._scene.objects():
95 if obj._loadAnim is not None:
99 def sceneUpdated(self):
100 self._sceneUpdateTimer.Start(1, True)
101 self._slicer.abortSlicer()
104 def _updateSliceProgress(self, progressValue, ready):
105 self.printButton.setDisabled(not ready)
106 self.printButton.setProgressBar(progressValue)
109 def loadScene(self, fileList):
110 for filename in fileList:
111 for obj in meshLoader.loadMeshes(filename):
112 obj._loadAnim = anim(1, 0, 1.5)
114 self._selectObject(obj)
117 def _deleteObject(self, obj):
118 if obj == self._selectedObj:
119 self._selectedObj = None
120 if obj == self._focusObj:
121 self._focusObj = None
122 self._scene.remove(obj)
123 for m in obj._meshList:
124 if m.vbo is not None:
125 self.glReleaseList.append(m.vbo)
126 if self._isSimpleMode:
127 self._scene.arrangeAll()
130 def _selectObject(self, obj, zoom = True):
131 if obj != self._selectedObj:
132 self._selectedObj = obj
134 newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
135 self._animView = anim(self._viewTarget.copy(), newViewPos, 0.5)
136 newZoom = obj.getBoundaryCircle() * 6
137 if newZoom > numpy.max(self._machineSize) * 3:
138 newZoom = numpy.max(self._machineSize) * 3
139 self._animZoom = anim(self._zoom, newZoom, 0.5)
141 def updateProfileToControls(self):
142 oldSimpleMode = self._isSimpleMode
143 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
144 if self._isSimpleMode and not oldSimpleMode:
145 self._scene.arrangeAll()
147 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
148 self._objColors[0] = profile.getPreferenceColour('model_colour')
149 self._objColors[1] = profile.getPreferenceColour('model_colour2')
150 self._objColors[2] = profile.getPreferenceColour('model_colour3')
151 self._objColors[3] = profile.getPreferenceColour('model_colour4')
152 self._scene.setMachineSize(self._machineSize)
154 def OnKeyChar(self, keyCode):
155 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
156 if self._selectedObj is not None:
157 self._deleteObject(self._selectedObj)
160 if keyCode == wx.WXK_F3:
161 shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
163 def ShaderUpdate(self, v, f):
164 s = opengl.GLShader(v, f)
166 self._objectLoadShader.release()
167 self._objectLoadShader = s
168 for obj in self._scene.objects():
169 obj._loadAnim = anim(1, 0, 1.5)
172 def OnMouseDown(self,e):
173 self._mouseX = e.GetX()
174 self._mouseY = e.GetY()
175 self._mouseClick3DPos = self._mouse3Dpos
177 self._mouseState = 'doubleClick'
179 self._mouseState = 'dragOrClick'
180 if self._mouseState == 'dragOrClick':
182 if self._focusObj is not None:
183 self._selectObject(self._focusObj, False)
186 def OnMouseUp(self, e):
187 if self._mouseState == 'dragOrClick':
189 if self._focusObj is not None:
190 self._selectObject(self._focusObj)
192 self._selectedObj = None
194 if self._mouseState == 'dragObject' and self._selectedObj is not None:
195 self._scene.pushFree()
197 self._mouseState = None
199 def OnMouseMotion(self,e):
200 if e.Dragging() and self._mouseState is not None:
201 self._mouseState = 'drag'
202 if not e.LeftIsDown() and e.RightIsDown():
203 self._yaw += e.GetX() - self._mouseX
204 self._pitch -= e.GetY() - self._mouseY
205 if self._pitch > 170:
209 elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
210 self._zoom += e.GetY() - self._mouseY
213 if self._zoom > numpy.max(self._machineSize) * 3:
214 self._zoom = numpy.max(self._machineSize) * 3
215 elif e.LeftIsDown() and self._selectedObj is not None and not self._isSimpleMode:
216 self._mouseState = 'dragObject'
217 z = max(0, self._mouseClick3DPos[2])
218 p0 = opengl.unproject(self._mouseX, self.viewport[1] + self.viewport[3] - self._mouseY, 0, self.modelMatrix, self.projMatrix, self.viewport)
219 p1 = opengl.unproject(self._mouseX, self.viewport[1] + self.viewport[3] - self._mouseY, 1, self.modelMatrix, self.projMatrix, self.viewport)
220 p2 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 0, self.modelMatrix, self.projMatrix, self.viewport)
221 p3 = opengl.unproject(e.GetX(), self.viewport[1] + self.viewport[3] - e.GetY(), 1, self.modelMatrix, self.projMatrix, self.viewport)
226 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
227 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
228 diff = cursorZ1 - cursorZ0
229 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
231 self._mouseX = e.GetX()
232 self._mouseY = e.GetY()
234 def _init3DView(self):
235 # set viewing projection
236 size = self.GetSize()
237 glViewport(0, 0, size.GetWidth(), size.GetHeight())
240 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
242 glDisable(GL_RESCALE_NORMAL)
243 glDisable(GL_LIGHTING)
245 glEnable(GL_DEPTH_TEST)
246 glDisable(GL_CULL_FACE)
248 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
250 glClearColor(0.8, 0.8, 0.8, 1.0)
254 glMatrixMode(GL_PROJECTION)
256 aspect = float(size.GetWidth()) / float(size.GetHeight())
257 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
259 glMatrixMode(GL_MODELVIEW)
261 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
264 if self._animView is not None:
265 self._viewTarget = self._animView.getPosition()
266 if self._animView.isDone():
267 self._animView = None
268 if self._animZoom is not None:
269 self._zoom = self._animZoom.getPosition()
270 if self._animZoom.isDone():
271 self._animZoom = None
272 if self._objectShader is None:
273 self._objectShader = opengl.GLShader("""
274 uniform float cameraDistance;
275 varying float light_amount;
279 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
280 gl_FrontColor = gl_Color;
282 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
283 light_amount *= 1 - (length(gl_Position.xyz - vec3(0,0,cameraDistance)) / 1.5 / cameraDistance);
287 uniform float cameraDistance;
288 varying float light_amount;
292 gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
295 self._objectLoadShader = opengl.GLShader("""
296 uniform float cameraDistance;
297 uniform float intensity;
299 varying float light_amount;
303 vec4 tmp = gl_Vertex;
304 tmp.x += sin(tmp.z/5+intensity*30) * scale * intensity;
305 tmp.y += sin(tmp.z/3+intensity*40) * scale * intensity;
306 gl_Position = gl_ModelViewProjectionMatrix * tmp;
307 gl_FrontColor = gl_Color;
309 light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
310 light_amount *= 1 - (length(gl_Position.xyz - vec3(0,0,cameraDistance)) / 1.5 / cameraDistance);
314 uniform float cameraDistance;
315 uniform float intensity;
316 varying float light_amount;
320 gl_FragColor = vec4(gl_Color.xyz * light_amount, 1-intensity);
324 glTranslate(0,0,-self._zoom)
325 glRotate(-self._pitch, 1,0,0)
326 glRotate(self._yaw, 0,0,1)
327 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
329 self.viewport = glGetIntegerv(GL_VIEWPORT)
330 self.modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
331 self.projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
333 glClearColor(1,1,1,1)
334 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
336 for n in xrange(0, len(self._scene.objects())):
337 obj = self._scene.objects()[n]
338 glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
339 self._renderObject(obj)
341 if self._mouseX > -1:
342 n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
343 if n < len(self._scene.objects()):
344 self._focusObj = self._scene.objects()[n]
346 self._focusObj = None
347 f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
348 self._mouse3Dpos = opengl.unproject(self._mouseX, self.viewport[1] + self.viewport[3] - self._mouseY, f, self.modelMatrix, self.projMatrix, self.viewport)
351 glTranslate(0,0,-self._zoom)
352 glRotate(-self._pitch, 1,0,0)
353 glRotate(self._yaw, 0,0,1)
354 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
356 glStencilFunc(GL_ALWAYS, 1, 1)
357 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
358 self._objectShader.bind()
359 self._objectShader.setUniform('cameraDistance', self._zoom)
360 for obj in self._scene.objects():
361 if obj._loadAnim is not None:
362 if obj._loadAnim.isDone():
366 col = self._objColors[0]
367 if not self._scene.checkPlatform(obj):
368 col = [0.5,0.5,0.5,0.8]
369 glDisable(GL_STENCIL_TEST)
370 if self._selectedObj == obj:
371 col = map(lambda n: n * 1.5, col)
372 glEnable(GL_STENCIL_TEST)
373 elif self._focusObj == obj:
374 col = map(lambda n: n * 1.2, col)
375 elif self._focusObj is not None or self._selectedObj is not None:
376 col = map(lambda n: n * 0.8, col)
377 glColor4f(col[0], col[1], col[2], col[3])
378 self._renderObject(obj)
379 self._objectShader.unbind()
381 glDisable(GL_STENCIL_TEST)
383 self._objectLoadShader.bind()
384 self._objectLoadShader.setUniform('cameraDistance', self._zoom)
385 glColor4f(0.2, 0.6, 1.0, 1.0)
386 for obj in self._scene.objects():
387 if obj._loadAnim is None:
389 self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
390 self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
391 self._renderObject(obj)
392 self._objectLoadShader.unbind()
397 #Draw the outline of the selected object, on top of everything else except the GUI.
398 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
399 glDisable(GL_DEPTH_TEST)
400 glEnable(GL_CULL_FACE)
401 glEnable(GL_STENCIL_TEST)
402 glStencilFunc(GL_EQUAL, 0, 255)
403 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
406 self._renderObject(self._selectedObj)
407 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
408 glDisable(GL_STENCIL_TEST)
409 glDisable(GL_CULL_FACE)
410 glEnable(GL_DEPTH_TEST)
412 def _renderObject(self, obj):
414 glTranslate(obj.getPosition()[0], obj.getPosition()[1], 0)
415 offset = obj.getDrawOffset()
416 glTranslate(-offset[0], -offset[1], -offset[2])
417 for m in obj._meshList:
419 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
423 def _drawMachine(self):
424 glEnable(GL_CULL_FACE)
427 if profile.getPreference('machine_type') == 'ultimaker':
429 self._objectShader.bind()
430 self._objectShader.setUniform('cameraDistance', self._zoom)
431 self._renderObject(self._platformMesh)
432 self._objectShader.unbind()
434 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
435 v0 = [ size[0] / 2, size[1] / 2, size[2]]
436 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
437 v2 = [-size[0] / 2, size[1] / 2, size[2]]
438 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
439 v4 = [ size[0] / 2, size[1] / 2, 0]
440 v5 = [ size[0] / 2,-size[1] / 2, 0]
441 v6 = [-size[0] / 2, size[1] / 2, 0]
442 v7 = [-size[0] / 2,-size[1] / 2, 0]
444 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
445 glEnableClientState(GL_VERTEX_ARRAY)
446 glVertexPointer(3, GL_FLOAT, 3*4, vList)
448 glColor4ub(5, 171, 231, 64)
449 glDrawArrays(GL_QUADS, 0, 4)
450 glColor4ub(5, 171, 231, 96)
451 glDrawArrays(GL_QUADS, 4, 8)
452 glColor4ub(5, 171, 231, 128)
453 glDrawArrays(GL_QUADS, 12, 8)
455 sx = self._machineSize[0]
456 sy = self._machineSize[1]
457 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
458 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
463 x1 = max(min(x1, sx/2), -sx/2)
464 y1 = max(min(y1, sy/2), -sy/2)
465 x2 = max(min(x2, sx/2), -sx/2)
466 y2 = max(min(y2, sy/2), -sy/2)
467 if (x & 1) == (y & 1):
468 glColor4ub(5, 171, 231, 127)
470 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
472 glVertex3f(x1, y1, -0.02)
473 glVertex3f(x2, y1, -0.02)
474 glVertex3f(x2, y2, -0.02)
475 glVertex3f(x1, y2, -0.02)
478 glDisableClientState(GL_VERTEX_ARRAY)
480 glDisable(GL_CULL_FACE)
482 class shaderEditor(wx.Dialog):
483 def __init__(self, parent, callback, v, f):
484 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
485 self._callback = callback
486 s = wx.BoxSizer(wx.VERTICAL)
488 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
489 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
490 s.Add(self._vertex, 1, flag=wx.EXPAND)
491 s.Add(self._fragment, 1, flag=wx.EXPAND)
493 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
494 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
496 self.SetPosition(self.GetParent().GetPosition())
497 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
501 self._callback(self._vertex.GetValue(), self._fragment.GetValue())