chiark / gitweb /
Move the OpenGL GUI base code to a seperate class for easier re-use.
[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         width = 0
195         for c in s:
196                 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
197         glBitmap(0,0,0,0, -width/2, 0, None)
198         for c in s:
199                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
200
201 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
202         npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
203         npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
204         finalMatrix = npModelMatrix * npProjMatrix
205         finalMatrix = numpy.linalg.inv(finalMatrix)
206
207         viewport = map(float, viewport)
208         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))
209         vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
210         ret = list(vector)[0:3] / vector[3]
211         return ret
212
213 def convert3x3MatrixTo4x4(matrix):
214         return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
215
216 def loadGLTexture(filename):
217         tex = glGenTextures(1)
218         glBindTexture(GL_TEXTURE_2D, tex)
219         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
220         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
221         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
222         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
223         img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
224         rgbData = img.GetData()
225         alphaData = img.GetAlphaData()
226         if alphaData != None:
227                 data = ''
228                 for i in xrange(0, len(alphaData)):
229                         data += rgbData[i*3:i*3+3] + alphaData[i]
230                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
231         else:
232                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
233         return tex
234
235 def ResetMatrixRotationAndScale():
236         matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
237         noZ = False
238         if matrix[3][2] > 0:
239                 return False
240         scale2D = matrix[0][0]
241         matrix[0][0] = 1.0
242         matrix[1][0] = 0.0
243         matrix[2][0] = 0.0
244         matrix[0][1] = 0.0
245         matrix[1][1] = 1.0
246         matrix[2][1] = 0.0
247         matrix[0][2] = 0.0
248         matrix[1][2] = 0.0
249         matrix[2][2] = 1.0
250
251         if matrix[3][2] != 0.0:
252                 matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100)
253                 matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100)
254                 matrix[3][2] = -100
255         else:
256                 matrix[0][0] = scale2D
257                 matrix[1][1] = scale2D
258                 matrix[2][2] = scale2D
259                 matrix[3][2] = -100
260                 noZ = True
261
262         glLoadMatrixf(matrix)
263         return noZ
264
265
266 def DrawBox(vMin, vMax):
267         glBegin(GL_LINE_LOOP)
268         glVertex3f(vMin[0], vMin[1], vMin[2])
269         glVertex3f(vMax[0], vMin[1], vMin[2])
270         glVertex3f(vMax[0], vMax[1], vMin[2])
271         glVertex3f(vMin[0], vMax[1], vMin[2])
272         glEnd()
273
274         glBegin(GL_LINE_LOOP)
275         glVertex3f(vMin[0], vMin[1], vMax[2])
276         glVertex3f(vMax[0], vMin[1], vMax[2])
277         glVertex3f(vMax[0], vMax[1], vMax[2])
278         glVertex3f(vMin[0], vMax[1], vMax[2])
279         glEnd()
280         glBegin(GL_LINES)
281         glVertex3f(vMin[0], vMin[1], vMin[2])
282         glVertex3f(vMin[0], vMin[1], vMax[2])
283         glVertex3f(vMax[0], vMin[1], vMin[2])
284         glVertex3f(vMax[0], vMin[1], vMax[2])
285         glVertex3f(vMax[0], vMax[1], vMin[2])
286         glVertex3f(vMax[0], vMax[1], vMax[2])
287         glVertex3f(vMin[0], vMax[1], vMin[2])
288         glVertex3f(vMin[0], vMax[1], vMax[2])
289         glEnd()
290
291
292 def DrawMeshOutline(mesh):
293         glEnable(GL_CULL_FACE)
294         glEnableClientState(GL_VERTEX_ARRAY);
295         glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
296
297         glCullFace(GL_FRONT)
298         glLineWidth(3)
299         glPolygonMode(GL_BACK, GL_LINE)
300         glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount)
301         glPolygonMode(GL_BACK, GL_FILL)
302         glCullFace(GL_BACK)
303
304         glDisableClientState(GL_VERTEX_ARRAY)
305
306
307 def DrawMesh(mesh):
308         glEnable(GL_CULL_FACE)
309         glEnableClientState(GL_VERTEX_ARRAY);
310         glEnableClientState(GL_NORMAL_ARRAY);
311         glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
312         glNormalPointer(GL_FLOAT, 0, mesh.normal)
313
314         #Odd, drawing in batchs is a LOT faster then drawing it all at once.
315         batchSize = 999    #Warning, batchSize needs to be dividable by 3
316         extraStartPos = int(mesh.vertexCount / batchSize) * batchSize
317         extraCount = mesh.vertexCount - extraStartPos
318
319         glCullFace(GL_BACK)
320         for i in xrange(0, int(mesh.vertexCount / batchSize)):
321                 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
322         glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
323
324         glCullFace(GL_FRONT)
325         glNormalPointer(GL_FLOAT, 0, mesh.invNormal)
326         for i in xrange(0, int(mesh.vertexCount / batchSize)):
327                 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
328         extraStartPos = int(mesh.vertexCount / batchSize) * batchSize
329         extraCount = mesh.vertexCount - extraStartPos
330         glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
331         glCullFace(GL_BACK)
332
333         glDisableClientState(GL_VERTEX_ARRAY)
334         glDisableClientState(GL_NORMAL_ARRAY);
335
336
337 def DrawMeshSteep(mesh, angle):
338         cosAngle = math.sin(angle / 180.0 * math.pi)
339         glDisable(GL_LIGHTING)
340         glDepthFunc(GL_EQUAL)
341         for i in xrange(0, int(mesh.vertexCount), 3):
342                 if mesh.normal[i][2] < -0.999999:
343                         if mesh.vertexes[i + 0][2] > 0.01:
344                                 glColor3f(0.5, 0, 0)
345                                 glBegin(GL_TRIANGLES)
346                                 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
347                                 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
348                                 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
349                                 glEnd()
350                 elif mesh.normal[i][2] < -cosAngle:
351                         glColor3f(-mesh.normal[i][2], 0, 0)
352                         glBegin(GL_TRIANGLES)
353                         glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
354                         glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
355                         glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
356                         glEnd()
357                 elif mesh.normal[i][2] > 0.999999:
358                         if mesh.vertexes[i + 0][2] > 0.01:
359                                 glColor3f(0.5, 0, 0)
360                                 glBegin(GL_TRIANGLES)
361                                 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
362                                 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
363                                 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
364                                 glEnd()
365                 elif mesh.normal[i][2] > cosAngle:
366                         glColor3f(mesh.normal[i][2], 0, 0)
367                         glBegin(GL_TRIANGLES)
368                         glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
369                         glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
370                         glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
371                         glEnd()
372         glDepthFunc(GL_LESS)
373
374
375 def DrawGCodeLayer(layer):
376         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
377         filamentArea = math.pi * filamentRadius * filamentRadius
378         lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
379
380         fillCycle = 0
381         fillColorCycle = [[0.5, 0.5, 0.0], [0.0, 0.5, 0.5], [0.5, 0.0, 0.5]]
382         moveColor = [0, 0, 1]
383         retractColor = [1, 0, 0.5]
384         supportColor = [0, 1, 1]
385         extrudeColor = [1, 0, 0]
386         innerWallColor = [0, 1, 0]
387         skirtColor = [0, 0.5, 0.5]
388         prevPathWasRetract = False
389
390         glDisable(GL_CULL_FACE)
391         for path in layer:
392                 if path.type == 'move':
393                         if prevPathWasRetract:
394                                 c = retractColor
395                         else:
396                                 c = moveColor
397                 zOffset = 0.01
398                 if path.type == 'extrude':
399                         if path.pathType == 'FILL':
400                                 c = fillColorCycle[fillCycle]
401                                 fillCycle = (fillCycle + 1) % len(fillColorCycle)
402                         elif path.pathType == 'WALL-INNER':
403                                 c = innerWallColor
404                                 zOffset = 0.02
405                         elif path.pathType == 'SUPPORT':
406                                 c = supportColor
407                         elif path.pathType == 'SKIRT':
408                                 c = skirtColor
409                         else:
410                                 c = extrudeColor
411                 if path.type == 'retract':
412                         c = [0, 1, 1]
413                 if path.type == 'extrude':
414                         drawLength = 0.0
415                         prevNormal = None
416                         for i in xrange(0, len(path.list) - 1):
417                                 v0 = path.list[i]
418                                 v1 = path.list[i + 1]
419
420                                 # Calculate line width from ePerDistance (needs layer thickness and filament diameter)
421                                 dist = (v0 - v1).vsize()
422                                 if dist > 0 and path.layerThickness > 0:
423                                         extrusionMMperDist = (v1.e - v0.e) / dist
424                                         lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply
425
426                                 drawLength += (v0 - v1).vsize()
427                                 normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1))
428                                 normal.normalize()
429
430                                 vv2 = v0 + normal * lineWidth
431                                 vv3 = v1 + normal * lineWidth
432                                 vv0 = v0 - normal * lineWidth
433                                 vv1 = v1 - normal * lineWidth
434
435                                 glBegin(GL_QUADS)
436                                 glColor3fv(c)
437                                 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
438                                 glVertex3f(vv1.x, vv1.y, vv1.z - zOffset)
439                                 glVertex3f(vv3.x, vv3.y, vv3.z - zOffset)
440                                 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
441                                 glEnd()
442                                 if prevNormal is not None:
443                                         n = (normal + prevNormal)
444                                         n.normalize()
445                                         vv4 = v0 + n * lineWidth
446                                         vv5 = v0 - n * lineWidth
447                                         glBegin(GL_QUADS)
448                                         glColor3fv(c)
449                                         glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
450                                         glVertex3f(vv4.x, vv4.y, vv4.z - zOffset)
451                                         glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset)
452                                         glVertex3f(v0.x, v0.y, v0.z - zOffset)
453
454                                         glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
455                                         glVertex3f(vv5.x, vv5.y, vv5.z - zOffset)
456                                         glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset)
457                                         glVertex3f(v0.x, v0.y, v0.z - zOffset)
458                                         glEnd()
459
460                                 prevNormal = normal
461                                 prevVv1 = vv1
462                                 prevVv3 = vv3
463                 else:
464                         glBegin(GL_LINE_STRIP)
465                         glColor3fv(c)
466                         for v in path.list:
467                                 glVertex3f(v.x, v.y, v.z)
468                         glEnd()
469                 if not path.type == 'move':
470                         prevPathWasRetract = False
471                 if path.type == 'retract' and path.list[0].almostEqual(path.list[-1]):
472                         prevPathWasRetract = True
473         glEnable(GL_CULL_FACE)