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