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 if self._program is not None:
45 shaders.glDeleteShader(self._vertexProgram)
46 shaders.glDeleteShader(self._fragmentProgram)
47 glDeleteProgram(self._program)
50 def setUniform(self, name, value):
51 if self._program is not None:
52 glUniform1f(glGetUniformLocation(self._program, name), value)
55 if self._program is not None and bool(glDeleteProgram):
56 print "Shader was not properly released!"
59 def __init__(self, vertexArray, normalArray):
60 self._buffer = glGenBuffers(1)
61 self._size = len(vertexArray)
62 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
63 glBufferData(GL_ARRAY_BUFFER, numpy.concatenate((vertexArray, normalArray), 1), GL_STATIC_DRAW)
64 glBindBuffer(GL_ARRAY_BUFFER, 0)
67 glEnableClientState(GL_VERTEX_ARRAY)
68 glEnableClientState(GL_NORMAL_ARRAY)
69 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
70 glVertexPointer(3, GL_FLOAT, 2*3*4, c_void_p(0))
71 glNormalPointer(GL_FLOAT, 2*3*4, c_void_p(3 * 4))
73 batchSize = 999 #Warning, batchSize needs to be dividable by 3
74 extraStartPos = int(self._size / batchSize) * batchSize
75 extraCount = self._size - extraStartPos
77 for i in xrange(0, int(self._size / batchSize)):
78 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
79 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
80 glBindBuffer(GL_ARRAY_BUFFER, 0)
81 glDisableClientState(GL_VERTEX_ARRAY)
82 glDisableClientState(GL_NORMAL_ARRAY)
85 if self._buffer is not None:
86 glDeleteBuffers(1, [self._buffer])
90 if self._buffer is not None and bool(glDeleteBuffers):
91 print "VBO was not properly released!"
93 def DrawMachine(machineSize):
94 glDisable(GL_LIGHTING)
95 glDisable(GL_CULL_FACE)
97 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
101 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
102 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
107 x1 = max(min(x1, sx), 0)
108 y1 = max(min(y1, sy), 0)
109 x2 = max(min(x2, sx), 0)
110 y2 = max(min(y2, sy), 0)
111 if (x & 1) == (y & 1):
112 glColor4ub(5, 171, 231, 127)
114 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
116 glVertex3f(x1, y1, -0.02)
117 glVertex3f(x2, y1, -0.02)
118 glVertex3f(x2, y2, -0.02)
119 glVertex3f(x1, y2, -0.02)
122 glEnable(GL_CULL_FACE)
124 if profile.getPreference('machine_type') == 'ultimaker':
126 glEnable(GL_LIGHTING)
127 glTranslate(100, 200, -1)
128 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8])
129 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5])
131 glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR)
134 if platformMesh is None:
136 platformMesh = meshLoader.loadMesh(getPathForMesh('ultimaker_platform.stl'))
141 DrawMesh(platformMesh)
143 glDisable(GL_LIGHTING)
144 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
146 glColor4ub(5, 171, 231, 64)
148 glVertex3f(0, 0, machineSize.z)
149 glVertex3f(0, machineSize.y, machineSize.z)
150 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
151 glVertex3f(machineSize.x, 0, machineSize.z)
154 glColor4ub(5, 171, 231, 96)
157 glVertex3f(0, 0, machineSize.z)
158 glVertex3f(machineSize.x, 0, machineSize.z)
159 glVertex3f(machineSize.x, 0, 0)
161 glVertex3f(0, machineSize.y, machineSize.z)
162 glVertex3f(0, machineSize.y, 0)
163 glVertex3f(machineSize.x, machineSize.y, 0)
164 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
167 glColor4ub(5, 171, 231, 128)
169 glVertex3f(0, 0, machineSize.z)
171 glVertex3f(0, machineSize.y, 0)
172 glVertex3f(0, machineSize.y, machineSize.z)
174 glVertex3f(machineSize.x, 0, 0)
175 glVertex3f(machineSize.x, 0, machineSize.z)
176 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
177 glVertex3f(machineSize.x, machineSize.y, 0)
182 #Draw the X/Y/Z indicator
202 glDisable(GL_DEPTH_TEST)
206 glTranslate(20, 0, 0)
207 noZ = ResetMatrixRotationAndScale()
208 glDrawStringCenter("X")
214 glTranslate(0, 20, 0)
215 glDrawStringCenter("Y")
222 glTranslate(0, 0, 20)
223 glDrawStringCenter("Z")
227 glEnable(GL_DEPTH_TEST)
229 def glDrawStringCenter(s):
231 glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
233 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
235 def glGetStringSize(s):
238 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
242 def glDrawStringLeft(s):
245 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
247 def glDrawStringRight(s):
249 glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
251 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
253 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
254 tx = float(texID % 4) / 4
255 ty = float(int(texID / 4)) / 8
265 glTranslatef(x, y, 0)
266 glEnable(GL_TEXTURE_2D)
268 glTexCoord2f(tx+tsx, ty)
272 glTexCoord2f(tx, ty+tsy)
274 glTexCoord2f(tx+tsx, ty+tsy)
279 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
280 npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
281 npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
282 finalMatrix = npModelMatrix * npProjMatrix
283 finalMatrix = numpy.linalg.inv(finalMatrix)
285 viewport = map(float, viewport)
286 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))
287 vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
288 ret = list(vector)[0:3] / vector[3]
291 def convert3x3MatrixTo4x4(matrix):
292 return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
294 def loadGLTexture(filename):
295 tex = glGenTextures(1)
296 glBindTexture(GL_TEXTURE_2D, tex)
297 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
298 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
299 img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
300 rgbData = img.GetData()
301 alphaData = img.GetAlphaData()
302 if alphaData is not None:
304 for i in xrange(0, len(alphaData)):
305 data += rgbData[i*3:i*3+3] + alphaData[i]
306 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
308 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
311 def ResetMatrixRotationAndScale():
312 matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
316 scale2D = matrix[0][0]
327 if matrix[3][2] != 0.0:
328 matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100)
329 matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100)
332 matrix[0][0] = scale2D
333 matrix[1][1] = scale2D
334 matrix[2][2] = scale2D
338 glLoadMatrixf(matrix)
342 def DrawBox(vMin, vMax):
343 glBegin(GL_LINE_LOOP)
344 glVertex3f(vMin[0], vMin[1], vMin[2])
345 glVertex3f(vMax[0], vMin[1], vMin[2])
346 glVertex3f(vMax[0], vMax[1], vMin[2])
347 glVertex3f(vMin[0], vMax[1], vMin[2])
350 glBegin(GL_LINE_LOOP)
351 glVertex3f(vMin[0], vMin[1], vMax[2])
352 glVertex3f(vMax[0], vMin[1], vMax[2])
353 glVertex3f(vMax[0], vMax[1], vMax[2])
354 glVertex3f(vMin[0], vMax[1], vMax[2])
357 glVertex3f(vMin[0], vMin[1], vMin[2])
358 glVertex3f(vMin[0], vMin[1], vMax[2])
359 glVertex3f(vMax[0], vMin[1], vMin[2])
360 glVertex3f(vMax[0], vMin[1], vMax[2])
361 glVertex3f(vMax[0], vMax[1], vMin[2])
362 glVertex3f(vMax[0], vMax[1], vMax[2])
363 glVertex3f(vMin[0], vMax[1], vMin[2])
364 glVertex3f(vMin[0], vMax[1], vMax[2])
368 def DrawMeshOutline(mesh):
369 glEnable(GL_CULL_FACE)
370 glEnableClientState(GL_VERTEX_ARRAY);
371 glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
375 glPolygonMode(GL_BACK, GL_LINE)
376 glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount)
377 glPolygonMode(GL_BACK, GL_FILL)
380 glDisableClientState(GL_VERTEX_ARRAY)
383 def DrawMesh(mesh, insideOut = False):
384 glEnable(GL_CULL_FACE)
385 glEnableClientState(GL_VERTEX_ARRAY)
386 glEnableClientState(GL_NORMAL_ARRAY)
387 for m in mesh._meshList:
388 glVertexPointer(3, GL_FLOAT, 0, m.vertexes)
390 glNormalPointer(GL_FLOAT, 0, m.invNormal)
392 glNormalPointer(GL_FLOAT, 0, m.normal)
394 #Odd, drawing in batchs is a LOT faster then drawing it all at once.
395 batchSize = 999 #Warning, batchSize needs to be dividable by 3
396 extraStartPos = int(m.vertexCount / batchSize) * batchSize
397 extraCount = m.vertexCount - extraStartPos
400 for i in xrange(0, int(m.vertexCount / batchSize)):
401 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
402 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
406 glNormalPointer(GL_FLOAT, 0, m.normal)
408 glNormalPointer(GL_FLOAT, 0, m.invNormal)
409 for i in xrange(0, int(m.vertexCount / batchSize)):
410 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
411 extraStartPos = int(m.vertexCount / batchSize) * batchSize
412 extraCount = m.vertexCount - extraStartPos
413 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
416 glDisableClientState(GL_VERTEX_ARRAY)
417 glDisableClientState(GL_NORMAL_ARRAY)
420 def DrawMeshSteep(mesh, matrix, angle):
421 cosAngle = math.sin(angle / 180.0 * math.pi)
422 glDisable(GL_LIGHTING)
423 glDepthFunc(GL_EQUAL)
424 normals = (numpy.matrix(mesh.normal, copy = False) * matrix).getA()
425 for i in xrange(0, int(mesh.vertexCount), 3):
426 if normals[i][2] < -0.999999:
427 if mesh.vertexes[i + 0][2] > 0.01:
429 glBegin(GL_TRIANGLES)
430 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
431 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
432 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
434 elif normals[i][2] < -cosAngle:
435 glColor3f(-normals[i][2], 0, 0)
436 glBegin(GL_TRIANGLES)
437 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
438 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
439 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
441 elif normals[i][2] > 0.999999:
442 if mesh.vertexes[i + 0][2] > 0.01:
444 glBegin(GL_TRIANGLES)
445 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
446 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
447 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
449 elif normals[i][2] > cosAngle:
450 glColor3f(normals[i][2], 0, 0)
451 glBegin(GL_TRIANGLES)
452 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
453 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
454 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
459 def DrawGCodeLayer(layer, drawQuick = True):
460 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
461 filamentArea = math.pi * filamentRadius * filamentRadius
462 lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
465 fillColorCycle = [[0.5, 0.5, 0.0, 1], [0.0, 0.5, 0.5, 1], [0.5, 0.0, 0.5, 1]]
466 moveColor = [0, 0, 1, 0.5]
467 retractColor = [1, 0, 0.5, 0.5]
468 supportColor = [0, 1, 1, 1]
469 extrudeColor = [[1, 0, 0, 1], [0, 1, 1, 1], [1, 1, 0, 1], [1, 0, 1, 1]]
470 innerWallColor = [0, 1, 0, 1]
471 skirtColor = [0, 0.5, 0.5, 1]
472 prevPathWasRetract = False
474 glDisable(GL_CULL_FACE)
476 if path.type == 'move':
477 if prevPathWasRetract:
484 if path.type == 'extrude':
485 if path.pathType == 'FILL':
486 c = fillColorCycle[fillCycle]
487 fillCycle = (fillCycle + 1) % len(fillColorCycle)
490 elif path.pathType == 'WALL-INNER':
493 elif path.pathType == 'SUPPORT':
495 elif path.pathType == 'SKIRT':
498 c = extrudeColor[path.extruder]
499 if path.type == 'retract':
501 if path.type == 'extrude' and not drawQuick:
504 for i in xrange(0, len(path.list) - 1):
506 v1 = path.list[i + 1]
508 # Calculate line width from ePerDistance (needs layer thickness and filament diameter)
509 dist = (v0 - v1).vsize()
510 if dist > 0 and path.layerThickness > 0:
511 extrusionMMperDist = (v1.e - v0.e) / dist
512 lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply
514 drawLength += (v0 - v1).vsize()
515 normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1))
518 vv2 = v0 + normal * lineWidth
519 vv3 = v1 + normal * lineWidth
520 vv0 = v0 - normal * lineWidth
521 vv1 = v1 - normal * lineWidth
525 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
526 glVertex3f(vv1.x, vv1.y, vv1.z - zOffset)
527 glVertex3f(vv3.x, vv3.y, vv3.z - zOffset)
528 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
530 if prevNormal is not None:
531 n = (normal + prevNormal)
533 vv4 = v0 + n * lineWidth
534 vv5 = v0 - n * lineWidth
537 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
538 glVertex3f(vv4.x, vv4.y, vv4.z - zOffset)
539 glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset)
540 glVertex3f(v0.x, v0.y, v0.z - zOffset)
542 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
543 glVertex3f(vv5.x, vv5.y, vv5.z - zOffset)
544 glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset)
545 glVertex3f(v0.x, v0.y, v0.z - zOffset)
552 glBegin(GL_LINE_STRIP)
555 glVertex3f(v.x, v.y, v.z)
557 if not path.type == 'move':
558 prevPathWasRetract = False
559 if path.type == 'retract' and path.list[0].almostEqual(path.list[-1]):
560 prevPathWasRetract = True
561 glEnable(GL_CULL_FACE)