chiark / gitweb /
Added online stat collection, added dropdown 3D button.
[cura.git] / Cura / gui / util / opengl.py
1 # coding=utf-8
2 from __future__ import absolute_import
3
4 import math
5 import numpy
6 import wx
7
8 from Cura.util import meshLoader
9 from Cura.util import util3d
10 from Cura.util import profile
11 from Cura.util.resources import getPathForMesh, getPathForImage
12
13 import OpenGL
14
15 OpenGL.ERROR_CHECKING = False
16 from OpenGL.GLUT import *
17 from OpenGL.GLU import *
18 from OpenGL.GL import *
19 glutInit()
20
21 def InitGL(window, view3D, zoom):
22         # set viewing projection
23         glMatrixMode(GL_MODELVIEW)
24         glLoadIdentity()
25         size = window.GetSize()
26         glViewport(0, 0, size.GetWidth(), size.GetHeight())
27
28         glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
29         glLightfv(GL_LIGHT1, GL_POSITION, [1.0, 1.0, 1.0, 0.0])
30
31         glEnable(GL_RESCALE_NORMAL)
32         glEnable(GL_LIGHTING)
33         glEnable(GL_LIGHT0)
34         glEnable(GL_DEPTH_TEST)
35         glEnable(GL_CULL_FACE)
36         glDisable(GL_BLEND)
37
38         glClearColor(1.0, 1.0, 1.0, 1.0)
39         glClearStencil(0)
40         glClearDepth(1.0)
41
42         glMatrixMode(GL_PROJECTION)
43         glLoadIdentity()
44         aspect = float(size.GetWidth()) / float(size.GetHeight())
45         if view3D:
46                 gluPerspective(45.0, aspect, 1.0, 1000.0)
47         else:
48                 glOrtho(-aspect * (zoom), aspect * (zoom), -1.0 * (zoom), 1.0 * (zoom), -1000.0, 1000.0)
49
50         glMatrixMode(GL_MODELVIEW)
51         glLoadIdentity()
52         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
53
54 platformMesh = None
55
56 def DrawMachine(machineSize):
57         glDisable(GL_LIGHTING)
58         glDisable(GL_CULL_FACE)
59         glEnable(GL_BLEND)
60         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
61
62         sx = machineSize.x
63         sy = machineSize.y
64         for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
65                 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
66                         x1 = sx/2+x * 10
67                         x2 = x1 + 10
68                         y1 = sx/2+y * 10
69                         y2 = y1 + 10
70                         x1 = max(min(x1, sx), 0)
71                         y1 = max(min(y1, sy), 0)
72                         x2 = max(min(x2, sx), 0)
73                         y2 = max(min(y2, sy), 0)
74                         if (x & 1) == (y & 1):
75                                 glColor4ub(5, 171, 231, 127)
76                         else:
77                                 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
78                         glBegin(GL_QUADS)
79                         glVertex3f(x1, y1, -0.02)
80                         glVertex3f(x2, y1, -0.02)
81                         glVertex3f(x2, y2, -0.02)
82                         glVertex3f(x1, y2, -0.02)
83                         glEnd()
84
85         glEnable(GL_CULL_FACE)
86
87         if profile.getPreference('machine_type') == 'ultimaker':
88                 glPushMatrix()
89                 glEnable(GL_LIGHTING)
90                 glTranslate(100, 200, -1)
91                 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8])
92                 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5])
93                 glEnable(GL_BLEND)
94                 glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR)
95
96                 global platformMesh
97                 if platformMesh is None:
98                         try:
99                                 platformMesh = meshLoader.loadMesh(getPathForMesh('ultimaker_platform.stl'))
100                         except:
101                                 platformMesh = False
102
103                 if platformMesh:
104                         DrawMesh(platformMesh)
105                 glPopMatrix()
106                 glDisable(GL_LIGHTING)
107                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
108
109         glColor4ub(5, 171, 231, 64)
110         glBegin(GL_QUADS)
111         glVertex3f(0, 0, machineSize.z)
112         glVertex3f(0, machineSize.y, machineSize.z)
113         glVertex3f(machineSize.x, machineSize.y, machineSize.z)
114         glVertex3f(machineSize.x, 0, machineSize.z)
115         glEnd()
116
117         glColor4ub(5, 171, 231, 96)
118         glBegin(GL_QUADS)
119         glVertex3f(0, 0, 0)
120         glVertex3f(0, 0, machineSize.z)
121         glVertex3f(machineSize.x, 0, machineSize.z)
122         glVertex3f(machineSize.x, 0, 0)
123
124         glVertex3f(0, machineSize.y, machineSize.z)
125         glVertex3f(0, machineSize.y, 0)
126         glVertex3f(machineSize.x, machineSize.y, 0)
127         glVertex3f(machineSize.x, machineSize.y, machineSize.z)
128         glEnd()
129
130         glColor4ub(5, 171, 231, 128)
131         glBegin(GL_QUADS)
132         glVertex3f(0, 0, machineSize.z)
133         glVertex3f(0, 0, 0)
134         glVertex3f(0, machineSize.y, 0)
135         glVertex3f(0, machineSize.y, machineSize.z)
136
137         glVertex3f(machineSize.x, 0, 0)
138         glVertex3f(machineSize.x, 0, machineSize.z)
139         glVertex3f(machineSize.x, machineSize.y, machineSize.z)
140         glVertex3f(machineSize.x, machineSize.y, 0)
141         glEnd()
142
143         glDisable(GL_BLEND)
144
145         #Draw the X/Y/Z indicator
146         glPushMatrix()
147         glTranslate(5, 5, 2)
148         glLineWidth(2)
149         glColor3f(0.5, 0, 0)
150         glBegin(GL_LINES)
151         glVertex3f(0, 0, 0)
152         glVertex3f(20, 0, 0)
153         glEnd()
154         glColor3f(0, 0.5, 0)
155         glBegin(GL_LINES)
156         glVertex3f(0, 0, 0)
157         glVertex3f(0, 20, 0)
158         glEnd()
159         glColor3f(0, 0, 0.5)
160         glBegin(GL_LINES)
161         glVertex3f(0, 0, 0)
162         glVertex3f(0, 0, 20)
163         glEnd()
164
165         glDisable(GL_DEPTH_TEST)
166         #X
167         glColor3f(1, 0, 0)
168         glPushMatrix()
169         glTranslate(20, 0, 0)
170         noZ = ResetMatrixRotationAndScale()
171         glDrawStringCenter("X")
172         glPopMatrix()
173
174         #Y
175         glColor3f(0, 1, 0)
176         glPushMatrix()
177         glTranslate(0, 20, 0)
178         glDrawStringCenter("Y")
179         glPopMatrix()
180
181         #Z
182         if not noZ:
183                 glColor3f(0, 0, 1)
184                 glPushMatrix()
185                 glTranslate(0, 0, 20)
186                 glDrawStringCenter("Z")
187                 glPopMatrix()
188
189         glPopMatrix()
190         glEnable(GL_DEPTH_TEST)
191
192 def glDrawStringCenter(s):
193         glRasterPos2f(0, 0)
194         glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
195         for c in s:
196                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
197
198 def glGetStringSize(s):
199         width = 0
200         for c in s:
201                 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
202         height = 18
203         return width, height
204
205 def glDrawStringLeft(s):
206         glRasterPos2f(0, 0)
207         for c in s:
208                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
209
210 def glDrawStringRight(s):
211         glRasterPos2f(0, 0)
212         glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
213         for c in s:
214                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
215
216 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
217         npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
218         npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
219         finalMatrix = npModelMatrix * npProjMatrix
220         finalMatrix = numpy.linalg.inv(finalMatrix)
221
222         viewport = map(float, viewport)
223         vector = numpy.array([(winx - viewport[0]) / viewport[2] * 2.0 - 1.0, (winy - viewport[1]) / viewport[3] * 2.0 - 1.0, winz * 2.0 - 1.0, 1]).reshape((1,4))
224         vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
225         ret = list(vector)[0:3] / vector[3]
226         return ret
227
228 def convert3x3MatrixTo4x4(matrix):
229         return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
230
231 def loadGLTexture(filename):
232         tex = glGenTextures(1)
233         glBindTexture(GL_TEXTURE_2D, tex)
234         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
235         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
236         img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
237         rgbData = img.GetData()
238         alphaData = img.GetAlphaData()
239         if alphaData is not None:
240                 data = ''
241                 for i in xrange(0, len(alphaData)):
242                         data += rgbData[i*3:i*3+3] + alphaData[i]
243                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
244         else:
245                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
246         return tex
247
248 def ResetMatrixRotationAndScale():
249         matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
250         noZ = False
251         if matrix[3][2] > 0:
252                 return False
253         scale2D = matrix[0][0]
254         matrix[0][0] = 1.0
255         matrix[1][0] = 0.0
256         matrix[2][0] = 0.0
257         matrix[0][1] = 0.0
258         matrix[1][1] = 1.0
259         matrix[2][1] = 0.0
260         matrix[0][2] = 0.0
261         matrix[1][2] = 0.0
262         matrix[2][2] = 1.0
263
264         if matrix[3][2] != 0.0:
265                 matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100)
266                 matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100)
267                 matrix[3][2] = -100
268         else:
269                 matrix[0][0] = scale2D
270                 matrix[1][1] = scale2D
271                 matrix[2][2] = scale2D
272                 matrix[3][2] = -100
273                 noZ = True
274
275         glLoadMatrixf(matrix)
276         return noZ
277
278
279 def DrawBox(vMin, vMax):
280         glBegin(GL_LINE_LOOP)
281         glVertex3f(vMin[0], vMin[1], vMin[2])
282         glVertex3f(vMax[0], vMin[1], vMin[2])
283         glVertex3f(vMax[0], vMax[1], vMin[2])
284         glVertex3f(vMin[0], vMax[1], vMin[2])
285         glEnd()
286
287         glBegin(GL_LINE_LOOP)
288         glVertex3f(vMin[0], vMin[1], vMax[2])
289         glVertex3f(vMax[0], vMin[1], vMax[2])
290         glVertex3f(vMax[0], vMax[1], vMax[2])
291         glVertex3f(vMin[0], vMax[1], vMax[2])
292         glEnd()
293         glBegin(GL_LINES)
294         glVertex3f(vMin[0], vMin[1], vMin[2])
295         glVertex3f(vMin[0], vMin[1], vMax[2])
296         glVertex3f(vMax[0], vMin[1], vMin[2])
297         glVertex3f(vMax[0], vMin[1], vMax[2])
298         glVertex3f(vMax[0], vMax[1], vMin[2])
299         glVertex3f(vMax[0], vMax[1], vMax[2])
300         glVertex3f(vMin[0], vMax[1], vMin[2])
301         glVertex3f(vMin[0], vMax[1], vMax[2])
302         glEnd()
303
304
305 def DrawMeshOutline(mesh):
306         glEnable(GL_CULL_FACE)
307         glEnableClientState(GL_VERTEX_ARRAY);
308         glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
309
310         glCullFace(GL_FRONT)
311         glLineWidth(3)
312         glPolygonMode(GL_BACK, GL_LINE)
313         glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount)
314         glPolygonMode(GL_BACK, GL_FILL)
315         glCullFace(GL_BACK)
316
317         glDisableClientState(GL_VERTEX_ARRAY)
318
319
320 def DrawMesh(mesh, insideOut = False):
321         glEnable(GL_CULL_FACE)
322         glEnableClientState(GL_VERTEX_ARRAY);
323         glEnableClientState(GL_NORMAL_ARRAY);
324         glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
325         if insideOut:
326                 glNormalPointer(GL_FLOAT, 0, mesh.invNormal)
327         else:
328                 glNormalPointer(GL_FLOAT, 0, mesh.normal)
329
330         #Odd, drawing in batchs is a LOT faster then drawing it all at once.
331         batchSize = 999    #Warning, batchSize needs to be dividable by 3
332         extraStartPos = int(mesh.vertexCount / batchSize) * batchSize
333         extraCount = mesh.vertexCount - extraStartPos
334
335         glCullFace(GL_BACK)
336         for i in xrange(0, int(mesh.vertexCount / batchSize)):
337                 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
338         glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
339
340         glCullFace(GL_FRONT)
341         if insideOut:
342                 glNormalPointer(GL_FLOAT, 0, mesh.normal)
343         else:
344                 glNormalPointer(GL_FLOAT, 0, mesh.invNormal)
345         for i in xrange(0, int(mesh.vertexCount / batchSize)):
346                 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
347         extraStartPos = int(mesh.vertexCount / batchSize) * batchSize
348         extraCount = mesh.vertexCount - extraStartPos
349         glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
350         glCullFace(GL_BACK)
351
352         glDisableClientState(GL_VERTEX_ARRAY)
353         glDisableClientState(GL_NORMAL_ARRAY)
354
355
356 def DrawMeshSteep(mesh, matrix, angle):
357         cosAngle = math.sin(angle / 180.0 * math.pi)
358         glDisable(GL_LIGHTING)
359         glDepthFunc(GL_EQUAL)
360         normals = (numpy.matrix(mesh.normal, copy = False) * matrix).getA()
361         for i in xrange(0, int(mesh.vertexCount), 3):
362                 if normals[i][2] < -0.999999:
363                         if mesh.vertexes[i + 0][2] > 0.01:
364                                 glColor3f(0.5, 0, 0)
365                                 glBegin(GL_TRIANGLES)
366                                 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
367                                 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
368                                 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
369                                 glEnd()
370                 elif normals[i][2] < -cosAngle:
371                         glColor3f(-normals[i][2], 0, 0)
372                         glBegin(GL_TRIANGLES)
373                         glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
374                         glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
375                         glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
376                         glEnd()
377                 elif normals[i][2] > 0.999999:
378                         if mesh.vertexes[i + 0][2] > 0.01:
379                                 glColor3f(0.5, 0, 0)
380                                 glBegin(GL_TRIANGLES)
381                                 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
382                                 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
383                                 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
384                                 glEnd()
385                 elif normals[i][2] > cosAngle:
386                         glColor3f(normals[i][2], 0, 0)
387                         glBegin(GL_TRIANGLES)
388                         glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
389                         glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
390                         glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
391                         glEnd()
392         glDepthFunc(GL_LESS)
393
394
395 def DrawGCodeLayer(layer):
396         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
397         filamentArea = math.pi * filamentRadius * filamentRadius
398         lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
399
400         fillCycle = 0
401         fillColorCycle = [[0.5, 0.5, 0.0, 1], [0.0, 0.5, 0.5, 1], [0.5, 0.0, 0.5, 1]]
402         moveColor = [0, 0, 1, 0.5]
403         retractColor = [1, 0, 0.5, 0.5]
404         supportColor = [0, 1, 1, 1]
405         extrudeColor = [1, 0, 0, 1]
406         innerWallColor = [0, 1, 0, 1]
407         skirtColor = [0, 0.5, 0.5, 1]
408         prevPathWasRetract = False
409
410         glDisable(GL_CULL_FACE)
411         for path in layer:
412                 if path.type == 'move':
413                         if prevPathWasRetract:
414                                 c = retractColor
415                         else:
416                                 c = moveColor
417                 zOffset = 0.01
418                 if path.type == 'extrude':
419                         if path.pathType == 'FILL':
420                                 c = fillColorCycle[fillCycle]
421                                 fillCycle = (fillCycle + 1) % len(fillColorCycle)
422                         elif path.pathType == 'WALL-INNER':
423                                 c = innerWallColor
424                                 zOffset = 0.02
425                         elif path.pathType == 'SUPPORT':
426                                 c = supportColor
427                         elif path.pathType == 'SKIRT':
428                                 c = skirtColor
429                         else:
430                                 c = extrudeColor
431                 if path.type == 'retract':
432                         c = retractColor
433                 if path.type == 'extrude':
434                         drawLength = 0.0
435                         prevNormal = None
436                         for i in xrange(0, len(path.list) - 1):
437                                 v0 = path.list[i]
438                                 v1 = path.list[i + 1]
439
440                                 # Calculate line width from ePerDistance (needs layer thickness and filament diameter)
441                                 dist = (v0 - v1).vsize()
442                                 if dist > 0 and path.layerThickness > 0:
443                                         extrusionMMperDist = (v1.e - v0.e) / dist
444                                         lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply
445
446                                 drawLength += (v0 - v1).vsize()
447                                 normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1))
448                                 normal.normalize()
449
450                                 vv2 = v0 + normal * lineWidth
451                                 vv3 = v1 + normal * lineWidth
452                                 vv0 = v0 - normal * lineWidth
453                                 vv1 = v1 - normal * lineWidth
454
455                                 glBegin(GL_QUADS)
456                                 glColor4fv(c)
457                                 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
458                                 glVertex3f(vv1.x, vv1.y, vv1.z - zOffset)
459                                 glVertex3f(vv3.x, vv3.y, vv3.z - zOffset)
460                                 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
461                                 glEnd()
462                                 if prevNormal is not None:
463                                         n = (normal + prevNormal)
464                                         n.normalize()
465                                         vv4 = v0 + n * lineWidth
466                                         vv5 = v0 - n * lineWidth
467                                         glBegin(GL_QUADS)
468                                         glColor4fv(c)
469                                         glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
470                                         glVertex3f(vv4.x, vv4.y, vv4.z - zOffset)
471                                         glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset)
472                                         glVertex3f(v0.x, v0.y, v0.z - zOffset)
473
474                                         glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
475                                         glVertex3f(vv5.x, vv5.y, vv5.z - zOffset)
476                                         glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset)
477                                         glVertex3f(v0.x, v0.y, v0.z - zOffset)
478                                         glEnd()
479
480                                 prevNormal = normal
481                                 prevVv1 = vv1
482                                 prevVv3 = vv3
483                 else:
484                         glBegin(GL_LINE_STRIP)
485                         glColor4fv(c)
486                         for v in path.list:
487                                 glVertex3f(v.x, v.y, v.z)
488                         glEnd()
489                 if not path.type == 'move':
490                         prevPathWasRetract = False
491                 if path.type == 'retract' and path.list[0].almostEqual(path.list[-1]):
492                         prevPathWasRetract = True
493         glEnable(GL_CULL_FACE)