2 from __future__ import absolute_import
9 from Cura.util import meshLoader
10 from Cura.util import util3d
11 from Cura.util import profile
12 from Cura.util.resources import getPathForMesh, getPathForImage
16 OpenGL.ERROR_CHECKING = False
17 from OpenGL.GLUT import *
18 from OpenGL.GLU import *
19 from OpenGL.GL import *
20 from OpenGL.GL import shaders
25 class GLShader(object):
26 def __init__(self, vertexProgram, fragmentProgram):
28 self._vertexProgram = shaders.compileShader(vertexProgram, GL_VERTEX_SHADER)
29 self._fragmentProgram = shaders.compileShader(fragmentProgram, GL_FRAGMENT_SHADER)
30 self._program = shaders.compileProgram(self._vertexProgram, self._fragmentProgram)
31 except RuntimeError, e:
37 if self._program is not None:
38 shaders.glUseProgram(self._program)
41 shaders.glUseProgram(0)
44 shaders.glDeleteShader(self._vertexProgram)
45 shaders.glDeleteShader(self._fragmentProgram)
46 glDeleteProgram(self._program)
48 def setUniform(self, name, value):
49 glUniform1f(glGetUniformLocation(self._program, name), value)
52 def __init__(self, vertexArray, normalArray):
53 self._buffer = glGenBuffers(1)
54 self._size = len(vertexArray)
55 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
56 glBufferData(GL_ARRAY_BUFFER, numpy.concatenate((vertexArray, normalArray), 1), GL_STATIC_DRAW)
57 glBindBuffer(GL_ARRAY_BUFFER, 0)
60 glEnableClientState(GL_VERTEX_ARRAY)
61 glEnableClientState(GL_NORMAL_ARRAY)
62 #glVertexPointer(3, GL_FLOAT, 0, m.vertexes)
63 #glNormalPointer(GL_FLOAT, 0, m.normal)
64 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
65 glVertexPointer(3, GL_FLOAT, 2*3*4, c_void_p(0))
66 glNormalPointer(GL_FLOAT, 2*3*4, c_void_p(3 * 4))
68 batchSize = 999 #Warning, batchSize needs to be dividable by 3
69 extraStartPos = int(self._size / batchSize) * batchSize
70 extraCount = self._size - extraStartPos
72 for i in xrange(0, int(self._size / batchSize)):
73 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
74 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
75 glBindBuffer(GL_ARRAY_BUFFER, 0)
76 glDisableClientState(GL_VERTEX_ARRAY)
77 glDisableClientState(GL_NORMAL_ARRAY)
80 if self._buffer is not None:
81 glDeleteBuffers(self._buffer, 1)
85 if self._buffer is not None:
86 print "OpenGL buffer was not properly cleaned, trying to clean it up now."
87 glDeleteBuffers(self._buffer, 1)
89 def DrawMachine(machineSize):
90 glDisable(GL_LIGHTING)
91 glDisable(GL_CULL_FACE)
93 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
97 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
98 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
103 x1 = max(min(x1, sx), 0)
104 y1 = max(min(y1, sy), 0)
105 x2 = max(min(x2, sx), 0)
106 y2 = max(min(y2, sy), 0)
107 if (x & 1) == (y & 1):
108 glColor4ub(5, 171, 231, 127)
110 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
112 glVertex3f(x1, y1, -0.02)
113 glVertex3f(x2, y1, -0.02)
114 glVertex3f(x2, y2, -0.02)
115 glVertex3f(x1, y2, -0.02)
118 glEnable(GL_CULL_FACE)
120 if profile.getPreference('machine_type') == 'ultimaker':
122 glEnable(GL_LIGHTING)
123 glTranslate(100, 200, -1)
124 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8])
125 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5])
127 glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR)
130 if platformMesh is None:
132 platformMesh = meshLoader.loadMesh(getPathForMesh('ultimaker_platform.stl'))
137 DrawMesh(platformMesh)
139 glDisable(GL_LIGHTING)
140 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
142 glColor4ub(5, 171, 231, 64)
144 glVertex3f(0, 0, machineSize.z)
145 glVertex3f(0, machineSize.y, machineSize.z)
146 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
147 glVertex3f(machineSize.x, 0, machineSize.z)
150 glColor4ub(5, 171, 231, 96)
153 glVertex3f(0, 0, machineSize.z)
154 glVertex3f(machineSize.x, 0, machineSize.z)
155 glVertex3f(machineSize.x, 0, 0)
157 glVertex3f(0, machineSize.y, machineSize.z)
158 glVertex3f(0, machineSize.y, 0)
159 glVertex3f(machineSize.x, machineSize.y, 0)
160 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
163 glColor4ub(5, 171, 231, 128)
165 glVertex3f(0, 0, machineSize.z)
167 glVertex3f(0, machineSize.y, 0)
168 glVertex3f(0, machineSize.y, machineSize.z)
170 glVertex3f(machineSize.x, 0, 0)
171 glVertex3f(machineSize.x, 0, machineSize.z)
172 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
173 glVertex3f(machineSize.x, machineSize.y, 0)
178 #Draw the X/Y/Z indicator
198 glDisable(GL_DEPTH_TEST)
202 glTranslate(20, 0, 0)
203 noZ = ResetMatrixRotationAndScale()
204 glDrawStringCenter("X")
210 glTranslate(0, 20, 0)
211 glDrawStringCenter("Y")
218 glTranslate(0, 0, 20)
219 glDrawStringCenter("Z")
223 glEnable(GL_DEPTH_TEST)
225 def glDrawStringCenter(s):
227 glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
229 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
231 def glGetStringSize(s):
234 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
238 def glDrawStringLeft(s):
241 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
243 def glDrawStringRight(s):
245 glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
247 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
249 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
250 tx = float(texID % 4) / 4
251 ty = float(int(texID / 4)) / 8
261 glTranslatef(x, y, 0)
262 glEnable(GL_TEXTURE_2D)
264 glTexCoord2f(tx+tsx, ty)
268 glTexCoord2f(tx, ty+tsy)
270 glTexCoord2f(tx+tsx, ty+tsy)
275 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
276 npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
277 npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
278 finalMatrix = npModelMatrix * npProjMatrix
279 finalMatrix = numpy.linalg.inv(finalMatrix)
281 viewport = map(float, viewport)
282 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))
283 vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
284 ret = list(vector)[0:3] / vector[3]
287 def convert3x3MatrixTo4x4(matrix):
288 return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
290 def loadGLTexture(filename):
291 tex = glGenTextures(1)
292 glBindTexture(GL_TEXTURE_2D, tex)
293 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
294 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
295 img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
296 rgbData = img.GetData()
297 alphaData = img.GetAlphaData()
298 if alphaData is not None:
300 for i in xrange(0, len(alphaData)):
301 data += rgbData[i*3:i*3+3] + alphaData[i]
302 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
304 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
307 def ResetMatrixRotationAndScale():
308 matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
312 scale2D = matrix[0][0]
323 if matrix[3][2] != 0.0:
324 matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100)
325 matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100)
328 matrix[0][0] = scale2D
329 matrix[1][1] = scale2D
330 matrix[2][2] = scale2D
334 glLoadMatrixf(matrix)
338 def DrawBox(vMin, vMax):
339 glBegin(GL_LINE_LOOP)
340 glVertex3f(vMin[0], vMin[1], vMin[2])
341 glVertex3f(vMax[0], vMin[1], vMin[2])
342 glVertex3f(vMax[0], vMax[1], vMin[2])
343 glVertex3f(vMin[0], vMax[1], vMin[2])
346 glBegin(GL_LINE_LOOP)
347 glVertex3f(vMin[0], vMin[1], vMax[2])
348 glVertex3f(vMax[0], vMin[1], vMax[2])
349 glVertex3f(vMax[0], vMax[1], vMax[2])
350 glVertex3f(vMin[0], vMax[1], vMax[2])
353 glVertex3f(vMin[0], vMin[1], vMin[2])
354 glVertex3f(vMin[0], vMin[1], vMax[2])
355 glVertex3f(vMax[0], vMin[1], vMin[2])
356 glVertex3f(vMax[0], vMin[1], vMax[2])
357 glVertex3f(vMax[0], vMax[1], vMin[2])
358 glVertex3f(vMax[0], vMax[1], vMax[2])
359 glVertex3f(vMin[0], vMax[1], vMin[2])
360 glVertex3f(vMin[0], vMax[1], vMax[2])
364 def DrawMeshOutline(mesh):
365 glEnable(GL_CULL_FACE)
366 glEnableClientState(GL_VERTEX_ARRAY);
367 glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
371 glPolygonMode(GL_BACK, GL_LINE)
372 glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount)
373 glPolygonMode(GL_BACK, GL_FILL)
376 glDisableClientState(GL_VERTEX_ARRAY)
379 def DrawMesh(mesh, insideOut = False):
380 glEnable(GL_CULL_FACE)
381 glEnableClientState(GL_VERTEX_ARRAY)
382 glEnableClientState(GL_NORMAL_ARRAY)
383 for m in mesh._meshList:
384 glVertexPointer(3, GL_FLOAT, 0, m.vertexes)
386 glNormalPointer(GL_FLOAT, 0, m.invNormal)
388 glNormalPointer(GL_FLOAT, 0, m.normal)
390 #Odd, drawing in batchs is a LOT faster then drawing it all at once.
391 batchSize = 999 #Warning, batchSize needs to be dividable by 3
392 extraStartPos = int(m.vertexCount / batchSize) * batchSize
393 extraCount = m.vertexCount - extraStartPos
396 for i in xrange(0, int(m.vertexCount / batchSize)):
397 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
398 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
402 glNormalPointer(GL_FLOAT, 0, m.normal)
404 glNormalPointer(GL_FLOAT, 0, m.invNormal)
405 for i in xrange(0, int(m.vertexCount / batchSize)):
406 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
407 extraStartPos = int(m.vertexCount / batchSize) * batchSize
408 extraCount = m.vertexCount - extraStartPos
409 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
412 glDisableClientState(GL_VERTEX_ARRAY)
413 glDisableClientState(GL_NORMAL_ARRAY)
416 def DrawMeshSteep(mesh, matrix, angle):
417 cosAngle = math.sin(angle / 180.0 * math.pi)
418 glDisable(GL_LIGHTING)
419 glDepthFunc(GL_EQUAL)
420 normals = (numpy.matrix(mesh.normal, copy = False) * matrix).getA()
421 for i in xrange(0, int(mesh.vertexCount), 3):
422 if normals[i][2] < -0.999999:
423 if mesh.vertexes[i + 0][2] > 0.01:
425 glBegin(GL_TRIANGLES)
426 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
427 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
428 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
430 elif normals[i][2] < -cosAngle:
431 glColor3f(-normals[i][2], 0, 0)
432 glBegin(GL_TRIANGLES)
433 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
434 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
435 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
437 elif normals[i][2] > 0.999999:
438 if mesh.vertexes[i + 0][2] > 0.01:
440 glBegin(GL_TRIANGLES)
441 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
442 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
443 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
445 elif normals[i][2] > cosAngle:
446 glColor3f(normals[i][2], 0, 0)
447 glBegin(GL_TRIANGLES)
448 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
449 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
450 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
455 def DrawGCodeLayer(layer, drawQuick = True):
456 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
457 filamentArea = math.pi * filamentRadius * filamentRadius
458 lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
461 fillColorCycle = [[0.5, 0.5, 0.0, 1], [0.0, 0.5, 0.5, 1], [0.5, 0.0, 0.5, 1]]
462 moveColor = [0, 0, 1, 0.5]
463 retractColor = [1, 0, 0.5, 0.5]
464 supportColor = [0, 1, 1, 1]
465 extrudeColor = [[1, 0, 0, 1], [0, 1, 1, 1], [1, 1, 0, 1], [1, 0, 1, 1]]
466 innerWallColor = [0, 1, 0, 1]
467 skirtColor = [0, 0.5, 0.5, 1]
468 prevPathWasRetract = False
470 glDisable(GL_CULL_FACE)
472 if path.type == 'move':
473 if prevPathWasRetract:
480 if path.type == 'extrude':
481 if path.pathType == 'FILL':
482 c = fillColorCycle[fillCycle]
483 fillCycle = (fillCycle + 1) % len(fillColorCycle)
486 elif path.pathType == 'WALL-INNER':
489 elif path.pathType == 'SUPPORT':
491 elif path.pathType == 'SKIRT':
494 c = extrudeColor[path.extruder]
495 if path.type == 'retract':
497 if path.type == 'extrude' and not drawQuick:
500 for i in xrange(0, len(path.list) - 1):
502 v1 = path.list[i + 1]
504 # Calculate line width from ePerDistance (needs layer thickness and filament diameter)
505 dist = (v0 - v1).vsize()
506 if dist > 0 and path.layerThickness > 0:
507 extrusionMMperDist = (v1.e - v0.e) / dist
508 lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply
510 drawLength += (v0 - v1).vsize()
511 normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1))
514 vv2 = v0 + normal * lineWidth
515 vv3 = v1 + normal * lineWidth
516 vv0 = v0 - normal * lineWidth
517 vv1 = v1 - normal * lineWidth
521 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
522 glVertex3f(vv1.x, vv1.y, vv1.z - zOffset)
523 glVertex3f(vv3.x, vv3.y, vv3.z - zOffset)
524 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
526 if prevNormal is not None:
527 n = (normal + prevNormal)
529 vv4 = v0 + n * lineWidth
530 vv5 = v0 - n * lineWidth
533 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
534 glVertex3f(vv4.x, vv4.y, vv4.z - zOffset)
535 glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset)
536 glVertex3f(v0.x, v0.y, v0.z - zOffset)
538 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
539 glVertex3f(vv5.x, vv5.y, vv5.z - zOffset)
540 glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset)
541 glVertex3f(v0.x, v0.y, v0.z - zOffset)
548 glBegin(GL_LINE_STRIP)
551 glVertex3f(v.x, v.y, v.z)
553 if not path.type == 'move':
554 prevPathWasRetract = False
555 if path.type == 'retract' and path.list[0].almostEqual(path.list[-1]):
556 prevPathWasRetract = True
557 glEnable(GL_CULL_FACE)