chiark / gitweb /
Merge branch 'SteamEngine' of github.com:daid/Cura into SteamEngine
[cura.git] / Cura / gui / sceneView.py
1 from __future__ import absolute_import
2
3 import wx
4 import numpy
5 import time
6 import os
7 import traceback
8 import shutil
9
10 import OpenGL
11 OpenGL.ERROR_CHECKING = False
12 from OpenGL.GLU import *
13 from OpenGL.GL import *
14
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 previewTools
24 from Cura.gui.util import opengl
25 from Cura.gui.util import openglGui
26
27 class anim(object):
28         def __init__(self, start, end, runTime):
29                 self._start = start
30                 self._end = end
31                 self._startTime = time.time()
32                 self._runTime = runTime
33
34         def isDone(self):
35                 return time.time() > self._startTime + self._runTime
36
37         def getPosition(self):
38                 if self.isDone():
39                         return self._end
40                 f = (time.time() - self._startTime) / self._runTime
41                 ts = f*f
42                 tc = f*f*f
43                 #f = 6*tc*ts + -15*ts*ts + 10*tc
44                 f = tc + -3*ts + 3*f
45                 return self._start + (self._end - self._start) * f
46
47 class SceneView(openglGui.glGuiPanel):
48         def __init__(self, parent):
49                 super(SceneView, self).__init__(parent)
50
51                 self._yaw = 30
52                 self._pitch = 60
53                 self._zoom = 300
54                 self._scene = objectScene.Scene()
55                 self._objectShader = None
56                 self._focusObj = None
57                 self._selectedObj = None
58                 self._objColors = [None,None,None,None]
59                 self._mouseX = -1
60                 self._mouseY = -1
61                 self._mouseState = None
62                 self._viewTarget = numpy.array([0,0,0], numpy.float32)
63                 self._animView = None
64                 self._animZoom = None
65                 self._platformMesh = meshLoader.loadMeshes(resources.getPathForMesh('ultimaker_platform.stl'))[0]
66                 self._platformMesh._drawOffset = numpy.array([0,0,0.5], numpy.float32)
67                 self._isSimpleMode = True
68
69                 self._viewport = None
70                 self._modelMatrix = None
71                 self._projMatrix = None
72                 self.tempMatrix = None
73
74                 self.openFileButton      = openglGui.glButton(self, 4, 'Load', (0,0), self.ShowLoadModel)
75                 self.printButton         = openglGui.glButton(self, 6, 'Print', (1,0), self.ShowPrintWindow)
76                 self.printButton.setDisabled(True)
77
78                 group = []
79                 self.rotateToolButton = openglGui.glRadioButton(self, 8, 'Rotate', (0,-1), group, self.OnToolSelect)
80                 self.scaleToolButton  = openglGui.glRadioButton(self, 9, 'Scale', (1,-1), group, self.OnToolSelect)
81                 self.mirrorToolButton  = openglGui.glRadioButton(self, 10, 'Mirror', (2,-1), group, self.OnToolSelect)
82
83                 self.resetRotationButton = openglGui.glButton(self, 12, 'Reset', (0,-2), self.OnRotateReset)
84                 self.layFlatButton       = openglGui.glButton(self, 16, 'Lay flat', (0,-3), self.OnLayFlat)
85
86                 self.resetScaleButton    = openglGui.glButton(self, 13, 'Reset', (1,-2), self.OnScaleReset)
87                 self.scaleMaxButton      = openglGui.glButton(self, 17, 'To max', (1,-3), self.OnScaleMax)
88
89                 self.mirrorXButton       = openglGui.glButton(self, 14, 'Mirror X', (2,-2), lambda button: self.OnMirror(0))
90                 self.mirrorYButton       = openglGui.glButton(self, 18, 'Mirror Y', (2,-3), lambda button: self.OnMirror(1))
91                 self.mirrorZButton       = openglGui.glButton(self, 22, 'Mirror Z', (2,-4), lambda button: self.OnMirror(2))
92
93                 self.rotateToolButton.setExpandArrow(True)
94                 self.scaleToolButton.setExpandArrow(True)
95                 self.mirrorToolButton.setExpandArrow(True)
96
97                 self.scaleForm = openglGui.glFrame(self, (2, -2))
98                 openglGui.glGuiLayoutGrid(self.scaleForm)
99                 openglGui.glLabel(self.scaleForm, 'Scale X', (0,0))
100                 self.scaleXctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,0), lambda value: self.OnScaleEntry(value, 0))
101                 openglGui.glLabel(self.scaleForm, 'Scale Y', (0,1))
102                 self.scaleYctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,1), lambda value: self.OnScaleEntry(value, 1))
103                 openglGui.glLabel(self.scaleForm, 'Scale Z', (0,2))
104                 self.scaleZctrl = openglGui.glNumberCtrl(self.scaleForm, '1.0', (1,2), lambda value: self.OnScaleEntry(value, 2))
105                 openglGui.glLabel(self.scaleForm, 'Size X (mm)', (0,4))
106                 self.scaleXmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,4), lambda value: self.OnScaleEntryMM(value, 0))
107                 openglGui.glLabel(self.scaleForm, 'Size Y (mm)', (0,5))
108                 self.scaleYmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,5), lambda value: self.OnScaleEntryMM(value, 1))
109                 openglGui.glLabel(self.scaleForm, 'Size Z (mm)', (0,6))
110                 self.scaleZmmctrl = openglGui.glNumberCtrl(self.scaleForm, '0.0', (1,6), lambda value: self.OnScaleEntryMM(value, 2))
111                 openglGui.glLabel(self.scaleForm, 'Uniform scale', (0,8))
112                 self.scaleUniform = openglGui.glCheckbox(self.scaleForm, True, (1,8), None)
113
114                 self._slicer = sliceEngine.Slicer(self._updateSliceProgress)
115                 self._sceneUpdateTimer = wx.Timer(self)
116                 self.Bind(wx.EVT_TIMER, lambda e : self._slicer.runSlicer(self._scene), self._sceneUpdateTimer)
117                 self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel)
118
119                 self.OnToolSelect(0)
120                 self.updateProfileToControls()
121                 wx.EVT_IDLE(self, self.OnIdle)
122
123         def ShowLoadModel(self, button):
124                 if button == 1:
125                         dlg=wx.FileDialog(self, 'Open 3D model', os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
126                         dlg.SetWildcard(meshLoader.wildcardFilter())
127                         if dlg.ShowModal() != wx.ID_OK:
128                                 dlg.Destroy()
129                                 return
130                         filename = dlg.GetPath()
131                         dlg.Destroy()
132                         if not(os.path.exists(filename)):
133                                 return False
134                         profile.putPreference('lastFile', filename)
135                         self.GetParent().GetParent().GetParent().addToModelMRU(filename)
136                         self.loadScene([filename])
137
138         def ShowPrintWindow(self, button):
139                 if button == 1:
140                         if machineCom.machineIsConnected():
141                                 printWindow.printFile(self._slicer.getGCodeFilename())
142                         elif len(removableStorage.getPossibleSDcardDrives()) > 0:
143                                 drives = removableStorage.getPossibleSDcardDrives()
144                                 if len(drives) > 1:
145                                         drive = drives[0]
146                                 else:
147                                         drive = drives[0]
148                                 filename = os.path.basename(profile.getPreference('lastFile'))
149                                 filename = filename[0:filename.rfind('.')] + '.gcode'
150                                 shutil.copy(self._slicer.getGCodeFilename(), drive[1] + filename)
151                         else:
152                                 defPath = profile.getPreference('lastFile')
153                                 defPath = defPath[0:defPath.rfind('.')] + '.gcode'
154                                 dlg=wx.FileDialog(self, 'Save toolpath', defPath, style=wx.FD_SAVE)
155                                 dlg.SetFilename(defPath)
156                                 dlg.SetWildcard('Toolpath (*.gcode)|*.gcode;*.g')
157                                 if dlg.ShowModal() != wx.ID_OK:
158                                         dlg.Destroy()
159                                         return
160                                 filename = dlg.GetPath()
161                                 dlg.Destroy()
162
163                                 shutil.copy(self._slicer.getGCodeFilename(), filename)
164
165         def OnIdle(self, e):
166                 if self._animView is not None or self._animZoom is not None:
167                         self.Refresh()
168                         return
169                 for obj in self._scene.objects():
170                         if obj._loadAnim is not None:
171                                 self.Refresh()
172                                 return
173
174         def OnToolSelect(self, button):
175                 if self.rotateToolButton.getSelected():
176                         self.tool = previewTools.toolRotate(self)
177                 elif self.scaleToolButton.getSelected():
178                         self.tool = previewTools.toolScale(self)
179                 elif self.mirrorToolButton.getSelected():
180                         self.tool = previewTools.toolNone(self)
181                 else:
182                         self.tool = previewTools.toolNone(self)
183                 self.resetRotationButton.setHidden(not self.rotateToolButton.getSelected())
184                 self.layFlatButton.setHidden(not self.rotateToolButton.getSelected())
185                 self.resetScaleButton.setHidden(not self.scaleToolButton.getSelected())
186                 self.scaleMaxButton.setHidden(not self.scaleToolButton.getSelected())
187                 self.scaleForm.setHidden(not self.scaleToolButton.getSelected())
188                 self.mirrorXButton.setHidden(not self.mirrorToolButton.getSelected())
189                 self.mirrorYButton.setHidden(not self.mirrorToolButton.getSelected())
190                 self.mirrorZButton.setHidden(not self.mirrorToolButton.getSelected())
191
192         def OnRotateReset(self, button):
193                 if self._selectedObj is None:
194                         return
195                 pass
196
197         def OnLayFlat(self, button):
198                 if self._selectedObj is None:
199                         return
200                 pass
201
202         def OnScaleReset(self, button):
203                 if self._selectedObj is None:
204                         return
205                 pass
206
207         def OnScaleMax(self, button):
208                 if self._selectedObj is None:
209                         return
210                 pass
211
212         def OnMirror(self, axis):
213                 if self._selectedObj is None:
214                         return
215                 self._selectedObj.mirror(axis)
216
217         def sceneUpdated(self):
218                 self._sceneUpdateTimer.Start(1, True)
219                 self._slicer.abortSlicer()
220                 self._scene.setSizeOffsets(numpy.array(profile.calculateObjectSizeOffsets(), numpy.float32))
221                 self.Refresh()
222
223         def _updateSliceProgress(self, progressValue, ready):
224                 self.printButton.setDisabled(not ready)
225                 self.printButton.setProgressBar(progressValue)
226                 self.Refresh()
227
228         def loadScene(self, fileList):
229                 for filename in fileList:
230                         try:
231                                 objList = meshLoader.loadMeshes(filename)
232                         except:
233                                 traceback.print_exc()
234                         else:
235                                 for obj in objList:
236                                         obj._loadAnim = anim(1, 0, 1.5)
237                                         self._scene.add(obj)
238                                         self._scene.centerAll()
239                                         self._selectObject(obj)
240                 self.sceneUpdated()
241
242         def _deleteObject(self, obj):
243                 if obj == self._selectedObj:
244                         self._selectedObj = None
245                 if obj == self._focusObj:
246                         self._focusObj = None
247                 self._scene.remove(obj)
248                 for m in obj._meshList:
249                         if m.vbo is not None:
250                                 self.glReleaseList.append(m.vbo)
251                 if self._isSimpleMode:
252                         self._scene.arrangeAll()
253                 self.sceneUpdated()
254
255         def _selectObject(self, obj, zoom = True):
256                 if obj != self._selectedObj:
257                         self._selectedObj = obj
258                 if zoom:
259                         newViewPos = numpy.array([obj.getPosition()[0], obj.getPosition()[1], obj.getMaximum()[2] / 2])
260                         self._animView = anim(self._viewTarget.copy(), newViewPos, 0.5)
261                         newZoom = obj.getBoundaryCircle() * 6
262                         if newZoom > numpy.max(self._machineSize) * 3:
263                                 newZoom = numpy.max(self._machineSize) * 3
264                         self._animZoom = anim(self._zoom, newZoom, 0.5)
265
266         def updateProfileToControls(self):
267                 oldSimpleMode = self._isSimpleMode
268                 self._isSimpleMode = profile.getPreference('startMode') == 'Simple'
269                 if self._isSimpleMode and not oldSimpleMode:
270                         self._scene.arrangeAll()
271                         self.sceneUpdated()
272                 self._machineSize = numpy.array([profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')])
273                 self._objColors[0] = profile.getPreferenceColour('model_colour')
274                 self._objColors[1] = profile.getPreferenceColour('model_colour2')
275                 self._objColors[2] = profile.getPreferenceColour('model_colour3')
276                 self._objColors[3] = profile.getPreferenceColour('model_colour4')
277                 self._scene.setMachineSize(self._machineSize)
278
279         def OnKeyChar(self, keyCode):
280                 if keyCode == wx.WXK_DELETE or keyCode == wx.WXK_NUMPAD_DELETE:
281                         if self._selectedObj is not None:
282                                 self._deleteObject(self._selectedObj)
283                                 self.Refresh()
284
285                 if keyCode == wx.WXK_F3 and wx.GetKeyState(wx.WXK_SHIFT):
286                         shaderEditor(self, self.ShaderUpdate, self._objectLoadShader.getVertexShader(), self._objectLoadShader.getFragmentShader())
287
288         def ShaderUpdate(self, v, f):
289                 s = opengl.GLShader(v, f)
290                 if s.isValid():
291                         self._objectLoadShader.release()
292                         self._objectLoadShader = s
293                         for obj in self._scene.objects():
294                                 obj._loadAnim = anim(1, 0, 1.5)
295                         self.Refresh()
296
297         def OnMouseDown(self,e):
298                 self._mouseX = e.GetX()
299                 self._mouseY = e.GetY()
300                 self._mouseClick3DPos = self._mouse3Dpos
301                 self._mouseClickFocus = self._focusObj
302                 if e.ButtonDClick():
303                         self._mouseState = 'doubleClick'
304                 else:
305                         self._mouseState = 'dragOrClick'
306                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
307                 p0 -= self.getObjectCenterPos() - self._viewTarget
308                 p1 -= self.getObjectCenterPos() - self._viewTarget
309                 if self.tool.OnDragStart(p0, p1):
310                         self._mouseState = 'tool'
311                 if self._mouseState == 'dragOrClick':
312                         if e.GetButton() == 1:
313                                 if self._focusObj is not None:
314                                         self._selectObject(self._focusObj, False)
315                                         self.Refresh()
316
317         def OnMouseUp(self, e):
318                 if e.LeftIsDown() or e.MiddleIsDown() or e.RightIsDown():
319                         return
320                 if self._mouseState == 'dragOrClick':
321                         if e.GetButton() == 1:
322                                 if self._focusObj is not None:
323                                         self._selectObject(self._focusObj)
324                                 else:
325                                         self._selectedObj = None
326                                         self.Refresh()
327                         if e.GetButton() == 3 and self._selectedObj == self._focusObj:
328                                 #menu = wx.Menu()
329                                 #menu.Append(-1, 'Test')
330                                 #self.PopupMenu(menu)
331                                 #menu.Destroy()
332                                 pass
333                 elif self._mouseState == 'dragObject' and self._selectedObj is not None:
334                         self._scene.pushFree()
335                         self.sceneUpdated()
336                 elif self._mouseState == 'tool':
337                         if self.tempMatrix is not None and self._selectedObj is not None:
338                                 self._selectedObj.applyMatrix(self.tempMatrix)
339                         self.tempMatrix = None
340                         self.tool.OnDragEnd()
341                         self.sceneUpdated()
342                 self._mouseState = None
343
344         def OnMouseMotion(self,e):
345                 p0, p1 = self.getMouseRay(e.GetX(), e.GetY())
346                 p0 -= self.getObjectCenterPos() - self._viewTarget
347                 p1 -= self.getObjectCenterPos() - self._viewTarget
348
349                 if e.Dragging() and self._mouseState is not None:
350                         if self._mouseState == 'tool':
351                                 self.tool.OnDrag(p0, p1)
352                         elif not e.LeftIsDown() and e.RightIsDown():
353                                 self._yaw += e.GetX() - self._mouseX
354                                 self._pitch -= e.GetY() - self._mouseY
355                                 if self._pitch > 170:
356                                         self._pitch = 170
357                                 if self._pitch < 10:
358                                         self._pitch = 10
359                         elif (e.LeftIsDown() and e.RightIsDown()) or e.MiddleIsDown():
360                                 self._zoom += e.GetY() - self._mouseY
361                                 if self._zoom < 1:
362                                         self._zoom = 1
363                                 if self._zoom > numpy.max(self._machineSize) * 3:
364                                         self._zoom = numpy.max(self._machineSize) * 3
365                         elif e.LeftIsDown() and self._selectedObj is not None and self._selectedObj == self._mouseClickFocus and not self._isSimpleMode:
366                                 self._mouseState = 'dragObject'
367                                 z = max(0, self._mouseClick3DPos[2])
368                                 p0, p1 = self.getMouseRay(self._mouseX, self._mouseY)
369                                 p2, p3 = self.getMouseRay(e.GetX(), e.GetY())
370                                 p0[2] -= z
371                                 p1[2] -= z
372                                 p2[2] -= z
373                                 p3[2] -= z
374                                 cursorZ0 = p0 - (p1 - p0) * (p0[2] / (p1[2] - p0[2]))
375                                 cursorZ1 = p2 - (p3 - p2) * (p2[2] / (p3[2] - p2[2]))
376                                 diff = cursorZ1 - cursorZ0
377                                 self._selectedObj.setPosition(self._selectedObj.getPosition() + diff[0:2])
378                 if not e.Dragging() or self._mouseState != 'tool':
379                         self.tool.OnMouseMove(p0, p1)
380
381                 self._mouseX = e.GetX()
382                 self._mouseY = e.GetY()
383
384         def OnMouseWheel(self, e):
385                 self._zoom *= 1.0 - float(e.GetWheelRotation() / e.GetWheelDelta()) / 10.0
386                 if self._zoom < 1.0:
387                         self._zoom = 1.0
388                 if self._zoom > numpy.max(self._machineSize) * 3:
389                         self._zoom = numpy.max(self._machineSize) * 3
390                 self.Refresh()
391
392         def getMouseRay(self, x, y):
393                 if self._viewport is None:
394                         return numpy.array([0,0,0],numpy.float32), numpy.array([0,0,1],numpy.float32)
395                 p0 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 0, self._modelMatrix, self._projMatrix, self._viewport)
396                 p1 = opengl.unproject(x, self._viewport[1] + self._viewport[3] - y, 1, self._modelMatrix, self._projMatrix, self._viewport)
397                 p0 -= self._viewTarget
398                 p1 -= self._viewTarget
399                 return p0, p1
400
401         def _init3DView(self):
402                 # set viewing projection
403                 size = self.GetSize()
404                 glViewport(0, 0, size.GetWidth(), size.GetHeight())
405                 glLoadIdentity()
406
407                 glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
408
409                 glDisable(GL_RESCALE_NORMAL)
410                 glDisable(GL_LIGHTING)
411                 glDisable(GL_LIGHT0)
412                 glEnable(GL_DEPTH_TEST)
413                 glDisable(GL_CULL_FACE)
414                 glDisable(GL_BLEND)
415                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
416
417                 glClearColor(0.8, 0.8, 0.8, 1.0)
418                 glClearStencil(0)
419                 glClearDepth(1.0)
420
421                 glMatrixMode(GL_PROJECTION)
422                 glLoadIdentity()
423                 aspect = float(size.GetWidth()) / float(size.GetHeight())
424                 gluPerspective(45.0, aspect, 1.0, numpy.max(self._machineSize) * 4)
425
426                 glMatrixMode(GL_MODELVIEW)
427                 glLoadIdentity()
428                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
429
430         def OnPaint(self,e):
431                 if machineCom.machineIsConnected():
432                         self.printButton._imageID = 6
433                         self.printButton._tooltip = 'Print'
434                 elif len(removableStorage.getPossibleSDcardDrives()) > 0:
435                         self.printButton._imageID = 2
436                         self.printButton._tooltip = 'Toolpath to SD'
437                 else:
438                         self.printButton._imageID = 3
439                         self.printButton._tooltip = 'Save toolpath'
440
441                 if self._animView is not None:
442                         self._viewTarget = self._animView.getPosition()
443                         if self._animView.isDone():
444                                 self._animView = None
445                 if self._animZoom is not None:
446                         self._zoom = self._animZoom.getPosition()
447                         if self._animZoom.isDone():
448                                 self._animZoom = None
449                 if self._objectShader is None:
450                         self._objectShader = opengl.GLShader("""
451 uniform float cameraDistance;
452 varying float light_amount;
453
454 void main(void)
455 {
456     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
457     gl_FrontColor = gl_Color;
458
459         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
460         light_amount *= 1.0 - (length(gl_Position.xyz - vec3(0.0,0.0,cameraDistance)) / 1.5 / cameraDistance);
461         light_amount += 0.2;
462 }
463                         ""","""
464 uniform float cameraDistance;
465 varying float light_amount;
466
467 void main(void)
468 {
469         gl_FragColor = vec4(gl_Color.xyz * light_amount, gl_Color[3]);
470 }
471                         """)
472                         self._objectLoadShader = opengl.GLShader("""
473 uniform float cameraDistance;
474 uniform float intensity;
475 uniform float scale;
476 varying float light_amount;
477
478 void main(void)
479 {
480         vec4 tmp = gl_Vertex;
481     tmp.x += sin(tmp.z/5.0+intensity*30.0) * scale * intensity;
482     tmp.y += sin(tmp.z/3.0+intensity*40.0) * scale * intensity;
483     gl_Position = gl_ModelViewProjectionMatrix * tmp;
484     gl_FrontColor = gl_Color;
485
486         light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
487         light_amount *= 1.0 - (length(gl_Position.xyz - vec3(0.0,0.0,cameraDistance)) / 1.5 / cameraDistance);
488         light_amount += 0.2;
489 }
490                         ""","""
491 uniform float cameraDistance;
492 uniform float intensity;
493 varying float light_amount;
494
495 void main(void)
496 {
497         gl_FragColor = vec4(gl_Color.xyz * light_amount, 1.0-intensity);
498 }
499                         """)
500                 self._init3DView()
501                 glTranslate(0,0,-self._zoom)
502                 glRotate(-self._pitch, 1,0,0)
503                 glRotate(self._yaw, 0,0,1)
504                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
505
506                 self._viewport = glGetIntegerv(GL_VIEWPORT)
507                 self._modelMatrix = glGetDoublev(GL_MODELVIEW_MATRIX)
508                 self._projMatrix = glGetDoublev(GL_PROJECTION_MATRIX)
509
510                 glClearColor(1,1,1,1)
511                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
512
513                 for n in xrange(0, len(self._scene.objects())):
514                         obj = self._scene.objects()[n]
515                         glColor4ub((n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF)
516                         self._renderObject(obj)
517
518                 if self._mouseX > -1:
519                         n = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8)[0][0]
520                         if n < len(self._scene.objects()):
521                                 self._focusObj = self._scene.objects()[n]
522                         else:
523                                 self._focusObj = None
524                         f = glReadPixels(self._mouseX, self.GetSize().GetHeight() - 1 - self._mouseY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0]
525                         self._mouse3Dpos = opengl.unproject(self._mouseX, self._viewport[1] + self._viewport[3] - self._mouseY, f, self._modelMatrix, self._projMatrix, self._viewport)
526                         self._mouse3Dpos -= self._viewTarget
527
528                 self._init3DView()
529                 glTranslate(0,0,-self._zoom)
530                 glRotate(-self._pitch, 1,0,0)
531                 glRotate(self._yaw, 0,0,1)
532                 glTranslate(-self._viewTarget[0],-self._viewTarget[1],-self._viewTarget[2])
533
534                 glStencilFunc(GL_ALWAYS, 1, 1)
535                 glStencilOp(GL_INCR, GL_INCR, GL_INCR)
536                 self._objectShader.bind()
537                 self._objectShader.setUniform('cameraDistance', self._zoom)
538                 for obj in self._scene.objects():
539                         if obj._loadAnim is not None:
540                                 if obj._loadAnim.isDone():
541                                         obj._loadAnim = None
542                                 else:
543                                         continue
544                         col = self._objColors[0]
545                         if not self._scene.checkPlatform(obj):
546                                 col = [0.5,0.5,0.5,0.8]
547                         glDisable(GL_STENCIL_TEST)
548                         if self._selectedObj == obj:
549                                 glEnable(GL_STENCIL_TEST)
550                         if self._focusObj == obj:
551                                 col = map(lambda n: n * 1.2, col)
552                         elif self._focusObj is not None or self._selectedObj is not None and obj != self._selectedObj:
553                                 col = map(lambda n: n * 0.8, col)
554                         glColor4f(col[0], col[1], col[2], col[3])
555                         self._renderObject(obj)
556                 self._objectShader.unbind()
557
558                 glDisable(GL_STENCIL_TEST)
559                 glEnable(GL_BLEND)
560                 self._objectLoadShader.bind()
561                 self._objectLoadShader.setUniform('cameraDistance', self._zoom)
562                 glColor4f(0.2, 0.6, 1.0, 1.0)
563                 for obj in self._scene.objects():
564                         if obj._loadAnim is None:
565                                 continue
566                         self._objectLoadShader.setUniform('intensity', obj._loadAnim.getPosition())
567                         self._objectLoadShader.setUniform('scale', obj.getBoundaryCircle() / 10)
568                         self._renderObject(obj)
569                 self._objectLoadShader.unbind()
570                 glDisable(GL_BLEND)
571
572                 self._drawMachine()
573
574                 #Draw the outline of the selected object, on top of everything else except the GUI.
575                 if self._selectedObj is not None and self._selectedObj._loadAnim is None:
576                         glDisable(GL_DEPTH_TEST)
577                         glEnable(GL_CULL_FACE)
578                         glEnable(GL_STENCIL_TEST)
579                         glStencilFunc(GL_EQUAL, 0, 255)
580                         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
581                         glLineWidth(2)
582                         glColor4f(1,1,1,0.5)
583                         self._renderObject(self._selectedObj)
584                         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
585                         glDisable(GL_STENCIL_TEST)
586                         glDisable(GL_CULL_FACE)
587                         glEnable(GL_DEPTH_TEST)
588
589                 if self._selectedObj is not None:
590                         glPushMatrix()
591                         pos = self.getObjectCenterPos()
592                         glTranslate(pos[0], pos[1], pos[2])
593                         self.tool.OnDraw()
594                         glPopMatrix()
595
596         def _renderObject(self, obj):
597                 glPushMatrix()
598                 glTranslate(obj.getPosition()[0], obj.getPosition()[1], obj.getSize()[2] / 2)
599
600                 if self.tempMatrix is not None and obj == self._selectedObj:
601                         tempMatrix = opengl.convert3x3MatrixTo4x4(self.tempMatrix)
602                         glMultMatrixf(tempMatrix)
603
604                 offset = obj.getDrawOffset()
605                 glTranslate(-offset[0], -offset[1], -offset[2] - obj.getSize()[2] / 2)
606
607                 tempMatrix = opengl.convert3x3MatrixTo4x4(obj.getMatrix())
608                 glMultMatrixf(tempMatrix)
609
610                 for m in obj._meshList:
611                         if m.vbo is None:
612                                 m.vbo = opengl.GLVBO(m.vertexes, m.normal)
613                         m.vbo.render()
614                 glPopMatrix()
615
616         def _drawMachine(self):
617                 glEnable(GL_CULL_FACE)
618                 glEnable(GL_BLEND)
619
620                 if profile.getPreference('machine_type') == 'ultimaker':
621                         glColor4f(1,1,1,0.5)
622                         self._objectShader.bind()
623                         self._objectShader.setUniform('cameraDistance', self._zoom)
624                         self._renderObject(self._platformMesh)
625                         self._objectShader.unbind()
626
627                 size = [profile.getPreferenceFloat('machine_width'), profile.getPreferenceFloat('machine_depth'), profile.getPreferenceFloat('machine_height')]
628                 v0 = [ size[0] / 2, size[1] / 2, size[2]]
629                 v1 = [ size[0] / 2,-size[1] / 2, size[2]]
630                 v2 = [-size[0] / 2, size[1] / 2, size[2]]
631                 v3 = [-size[0] / 2,-size[1] / 2, size[2]]
632                 v4 = [ size[0] / 2, size[1] / 2, 0]
633                 v5 = [ size[0] / 2,-size[1] / 2, 0]
634                 v6 = [-size[0] / 2, size[1] / 2, 0]
635                 v7 = [-size[0] / 2,-size[1] / 2, 0]
636
637                 vList = [v0,v1,v3,v2, v1,v0,v4,v5, v2,v3,v7,v6, v0,v2,v6,v4, v3,v1,v5,v7]
638                 glEnableClientState(GL_VERTEX_ARRAY)
639                 glVertexPointer(3, GL_FLOAT, 3*4, vList)
640
641                 glColor4ub(5, 171, 231, 64)
642                 glDrawArrays(GL_QUADS, 0, 4)
643                 glColor4ub(5, 171, 231, 96)
644                 glDrawArrays(GL_QUADS, 4, 8)
645                 glColor4ub(5, 171, 231, 128)
646                 glDrawArrays(GL_QUADS, 12, 8)
647
648                 sx = self._machineSize[0]
649                 sy = self._machineSize[1]
650                 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
651                         for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
652                                 x1 = x * 10
653                                 x2 = x1 + 10
654                                 y1 = y * 10
655                                 y2 = y1 + 10
656                                 x1 = max(min(x1, sx/2), -sx/2)
657                                 y1 = max(min(y1, sy/2), -sy/2)
658                                 x2 = max(min(x2, sx/2), -sx/2)
659                                 y2 = max(min(y2, sy/2), -sy/2)
660                                 if (x & 1) == (y & 1):
661                                         glColor4ub(5, 171, 231, 127)
662                                 else:
663                                         glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
664                                 glBegin(GL_QUADS)
665                                 glVertex3f(x1, y1, -0.02)
666                                 glVertex3f(x2, y1, -0.02)
667                                 glVertex3f(x2, y2, -0.02)
668                                 glVertex3f(x1, y2, -0.02)
669                                 glEnd()
670
671                 glDisableClientState(GL_VERTEX_ARRAY)
672                 glDisable(GL_BLEND)
673                 glDisable(GL_CULL_FACE)
674
675         def getObjectCenterPos(self):
676                 if self._selectedObj is None:
677                         return [0.0, 0.0, 0.0]
678                 pos = self._selectedObj.getPosition()
679                 size = self._selectedObj.getSize()
680                 return [pos[0], pos[1], size[2]/2]
681
682         def getObjectBoundaryCircle(self):
683                 if self._selectedObj is None:
684                         return 0.0
685                 return self._selectedObj.getBoundaryCircle()
686
687         def getObjectSize(self):
688                 if self._selectedObj is None:
689                         return [0.0, 0.0, 0.0]
690                 return self._selectedObj.getSize()
691
692         def getObjectMatrix(self):
693                 if self._selectedObj is None:
694                         return numpy.matrix([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])
695                 return self._selectedObj.getMatrix()
696
697 class shaderEditor(wx.Dialog):
698         def __init__(self, parent, callback, v, f):
699                 super(shaderEditor, self).__init__(parent, title="Shader editor", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
700                 self._callback = callback
701                 s = wx.BoxSizer(wx.VERTICAL)
702                 self.SetSizer(s)
703                 self._vertex = wx.TextCtrl(self, -1, v, style=wx.TE_MULTILINE)
704                 self._fragment = wx.TextCtrl(self, -1, f, style=wx.TE_MULTILINE)
705                 s.Add(self._vertex, 1, flag=wx.EXPAND)
706                 s.Add(self._fragment, 1, flag=wx.EXPAND)
707
708                 self._vertex.Bind(wx.EVT_TEXT, self.OnText, self._vertex)
709                 self._fragment.Bind(wx.EVT_TEXT, self.OnText, self._fragment)
710
711                 self.SetPosition(self.GetParent().GetPosition())
712                 self.SetSize((self.GetSize().GetWidth(), self.GetParent().GetSize().GetHeight()))
713                 self.Show()
714
715         def OnText(self, e):
716                 self._callback(self._vertex.GetValue(), self._fragment.GetValue())