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):
27 self._vertexString = vertexProgram
28 self._fragmentString = fragmentProgram
30 self._vertexProgram = shaders.compileShader(vertexProgram, GL_VERTEX_SHADER)
31 self._fragmentProgram = shaders.compileShader(fragmentProgram, GL_FRAGMENT_SHADER)
32 self._program = shaders.compileProgram(self._vertexProgram, self._fragmentProgram)
33 except RuntimeError, e:
38 if self._program is not None:
39 shaders.glUseProgram(self._program)
42 shaders.glUseProgram(0)
45 if self._program is not None:
46 shaders.glDeleteShader(self._vertexProgram)
47 shaders.glDeleteShader(self._fragmentProgram)
48 glDeleteProgram(self._program)
51 def setUniform(self, name, value):
52 if self._program is not None:
53 glUniform1f(glGetUniformLocation(self._program, name), value)
56 return self._program is not None
58 def getVertexShader(self):
59 return self._vertexString
61 def getFragmentShader(self):
62 return self._fragmentString
65 if self._program is not None and bool(glDeleteProgram):
66 print "Shader was not properly released!"
69 def __init__(self, vertexArray, normalArray):
70 self._buffer = glGenBuffers(1)
71 self._size = len(vertexArray)
72 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
73 glBufferData(GL_ARRAY_BUFFER, numpy.concatenate((vertexArray, normalArray), 1), GL_STATIC_DRAW)
74 glBindBuffer(GL_ARRAY_BUFFER, 0)
76 def render(self, render_type = GL_TRIANGLES):
77 glEnableClientState(GL_VERTEX_ARRAY)
78 glEnableClientState(GL_NORMAL_ARRAY)
79 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
80 glVertexPointer(3, GL_FLOAT, 2*3*4, c_void_p(0))
81 glNormalPointer(GL_FLOAT, 2*3*4, c_void_p(3 * 4))
83 batchSize = 999 #Warning, batchSize needs to be dividable by 3
84 extraStartPos = int(self._size / batchSize) * batchSize
85 extraCount = self._size - extraStartPos
87 for i in xrange(0, int(self._size / batchSize)):
88 glDrawArrays(render_type, i * batchSize, batchSize)
89 glDrawArrays(render_type, extraStartPos, extraCount)
90 glBindBuffer(GL_ARRAY_BUFFER, 0)
91 glDisableClientState(GL_VERTEX_ARRAY)
92 glDisableClientState(GL_NORMAL_ARRAY)
95 if self._buffer is not None:
96 glDeleteBuffers(1, [self._buffer])
100 if self._buffer is not None and bool(glDeleteBuffers):
101 print "VBO was not properly released!"
103 def DrawMachine(machineSize):
104 glDisable(GL_LIGHTING)
105 glDisable(GL_CULL_FACE)
107 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
111 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
112 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
117 x1 = max(min(x1, sx), 0)
118 y1 = max(min(y1, sy), 0)
119 x2 = max(min(x2, sx), 0)
120 y2 = max(min(y2, sy), 0)
121 if (x & 1) == (y & 1):
122 glColor4ub(5, 171, 231, 127)
124 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
126 glVertex3f(x1, y1, -0.02)
127 glVertex3f(x2, y1, -0.02)
128 glVertex3f(x2, y2, -0.02)
129 glVertex3f(x1, y2, -0.02)
132 glEnable(GL_CULL_FACE)
134 if profile.getPreference('machine_type') == 'ultimaker':
136 glEnable(GL_LIGHTING)
137 glTranslate(100, 200, -1)
138 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8])
139 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5])
141 glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR)
144 if platformMesh is None:
146 platformMesh = meshLoader.loadMesh(getPathForMesh('ultimaker_platform.stl'))
151 DrawMesh(platformMesh)
153 glDisable(GL_LIGHTING)
154 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
156 glColor4ub(5, 171, 231, 64)
158 glVertex3f(0, 0, machineSize.z)
159 glVertex3f(0, machineSize.y, machineSize.z)
160 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
161 glVertex3f(machineSize.x, 0, machineSize.z)
164 glColor4ub(5, 171, 231, 96)
167 glVertex3f(0, 0, machineSize.z)
168 glVertex3f(machineSize.x, 0, machineSize.z)
169 glVertex3f(machineSize.x, 0, 0)
171 glVertex3f(0, machineSize.y, machineSize.z)
172 glVertex3f(0, machineSize.y, 0)
173 glVertex3f(machineSize.x, machineSize.y, 0)
174 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
177 glColor4ub(5, 171, 231, 128)
179 glVertex3f(0, 0, machineSize.z)
181 glVertex3f(0, machineSize.y, 0)
182 glVertex3f(0, machineSize.y, machineSize.z)
184 glVertex3f(machineSize.x, 0, 0)
185 glVertex3f(machineSize.x, 0, machineSize.z)
186 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
187 glVertex3f(machineSize.x, machineSize.y, 0)
192 #Draw the X/Y/Z indicator
212 glDisable(GL_DEPTH_TEST)
216 glTranslate(20, 0, 0)
217 noZ = ResetMatrixRotationAndScale()
218 glDrawStringCenter("X")
224 glTranslate(0, 20, 0)
225 glDrawStringCenter("Y")
232 glTranslate(0, 0, 20)
233 glDrawStringCenter("Z")
237 glEnable(GL_DEPTH_TEST)
239 def glDrawStringCenter(s):
241 glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
243 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
245 def glGetStringSize(s):
248 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
252 def glDrawStringLeft(s):
255 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
257 def glDrawStringRight(s):
259 glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
261 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
263 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
264 tx = float(texID % 4) / 4
265 ty = float(int(texID / 4)) / 8
275 glTranslatef(x, y, 0)
276 glEnable(GL_TEXTURE_2D)
278 glTexCoord2f(tx+tsx, ty)
282 glTexCoord2f(tx, ty+tsy)
284 glTexCoord2f(tx+tsx, ty+tsy)
289 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
290 npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
291 npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
292 finalMatrix = npModelMatrix * npProjMatrix
293 finalMatrix = numpy.linalg.inv(finalMatrix)
295 viewport = map(float, viewport)
296 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))
297 vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
298 ret = list(vector)[0:3] / vector[3]
301 def convert3x3MatrixTo4x4(matrix):
302 return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
304 def loadGLTexture(filename):
305 tex = glGenTextures(1)
306 glBindTexture(GL_TEXTURE_2D, tex)
307 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
308 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
309 img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
310 rgbData = img.GetData()
311 alphaData = img.GetAlphaData()
312 if alphaData is not None:
314 for i in xrange(0, len(alphaData)):
315 data += rgbData[i*3:i*3+3] + alphaData[i]
316 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
318 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
321 def ResetMatrixRotationAndScale():
322 matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
326 scale2D = matrix[0][0]
337 if matrix[3][2] != 0.0:
338 matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100)
339 matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100)
342 matrix[0][0] = scale2D
343 matrix[1][1] = scale2D
344 matrix[2][2] = scale2D
348 glLoadMatrixf(matrix)
352 def DrawBox(vMin, vMax):
353 glBegin(GL_LINE_LOOP)
354 glVertex3f(vMin[0], vMin[1], vMin[2])
355 glVertex3f(vMax[0], vMin[1], vMin[2])
356 glVertex3f(vMax[0], vMax[1], vMin[2])
357 glVertex3f(vMin[0], vMax[1], vMin[2])
360 glBegin(GL_LINE_LOOP)
361 glVertex3f(vMin[0], vMin[1], vMax[2])
362 glVertex3f(vMax[0], vMin[1], vMax[2])
363 glVertex3f(vMax[0], vMax[1], vMax[2])
364 glVertex3f(vMin[0], vMax[1], vMax[2])
367 glVertex3f(vMin[0], vMin[1], vMin[2])
368 glVertex3f(vMin[0], vMin[1], vMax[2])
369 glVertex3f(vMax[0], vMin[1], vMin[2])
370 glVertex3f(vMax[0], vMin[1], vMax[2])
371 glVertex3f(vMax[0], vMax[1], vMin[2])
372 glVertex3f(vMax[0], vMax[1], vMax[2])
373 glVertex3f(vMin[0], vMax[1], vMin[2])
374 glVertex3f(vMin[0], vMax[1], vMax[2])
378 def DrawMeshOutline(mesh):
379 glEnable(GL_CULL_FACE)
380 glEnableClientState(GL_VERTEX_ARRAY);
381 glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
385 glPolygonMode(GL_BACK, GL_LINE)
386 glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount)
387 glPolygonMode(GL_BACK, GL_FILL)
390 glDisableClientState(GL_VERTEX_ARRAY)
393 def DrawMesh(mesh, insideOut = False):
394 glEnable(GL_CULL_FACE)
395 glEnableClientState(GL_VERTEX_ARRAY)
396 glEnableClientState(GL_NORMAL_ARRAY)
397 for m in mesh._meshList:
398 glVertexPointer(3, GL_FLOAT, 0, m.vertexes)
400 glNormalPointer(GL_FLOAT, 0, m.invNormal)
402 glNormalPointer(GL_FLOAT, 0, m.normal)
404 #Odd, drawing in batchs is a LOT faster then drawing it all at once.
405 batchSize = 999 #Warning, batchSize needs to be dividable by 3
406 extraStartPos = int(m.vertexCount / batchSize) * batchSize
407 extraCount = m.vertexCount - extraStartPos
410 for i in xrange(0, int(m.vertexCount / batchSize)):
411 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
412 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
416 glNormalPointer(GL_FLOAT, 0, m.normal)
418 glNormalPointer(GL_FLOAT, 0, m.invNormal)
419 for i in xrange(0, int(m.vertexCount / batchSize)):
420 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
421 extraStartPos = int(m.vertexCount / batchSize) * batchSize
422 extraCount = m.vertexCount - extraStartPos
423 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
426 glDisableClientState(GL_VERTEX_ARRAY)
427 glDisableClientState(GL_NORMAL_ARRAY)
430 def DrawMeshSteep(mesh, matrix, angle):
431 cosAngle = math.sin(angle / 180.0 * math.pi)
432 glDisable(GL_LIGHTING)
433 glDepthFunc(GL_EQUAL)
434 normals = (numpy.matrix(mesh.normal, copy = False) * matrix).getA()
435 for i in xrange(0, int(mesh.vertexCount), 3):
436 if normals[i][2] < -0.999999:
437 if mesh.vertexes[i + 0][2] > 0.01:
439 glBegin(GL_TRIANGLES)
440 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
441 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
442 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
444 elif normals[i][2] < -cosAngle:
445 glColor3f(-normals[i][2], 0, 0)
446 glBegin(GL_TRIANGLES)
447 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
448 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
449 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
451 elif normals[i][2] > 0.999999:
452 if mesh.vertexes[i + 0][2] > 0.01:
454 glBegin(GL_TRIANGLES)
455 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
456 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
457 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
459 elif normals[i][2] > cosAngle:
460 glColor3f(normals[i][2], 0, 0)
461 glBegin(GL_TRIANGLES)
462 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
463 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
464 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
469 def DrawGCodeLayer(layer, drawQuick = True):
470 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
471 filamentArea = math.pi * filamentRadius * filamentRadius
472 lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
475 fillColorCycle = [[0.5, 0.5, 0.0, 1], [0.0, 0.5, 0.5, 1], [0.5, 0.0, 0.5, 1]]
476 moveColor = [0, 0, 1, 0.5]
477 retractColor = [1, 0, 0.5, 0.5]
478 supportColor = [0, 1, 1, 1]
479 extrudeColor = [[1, 0, 0, 1], [0, 1, 1, 1], [1, 1, 0, 1], [1, 0, 1, 1]]
480 innerWallColor = [0, 1, 0, 1]
481 skirtColor = [0, 0.5, 0.5, 1]
482 prevPathWasRetract = False
484 glDisable(GL_CULL_FACE)
486 if path.type == 'move':
487 if prevPathWasRetract:
494 if path.type == 'extrude':
495 if path.pathType == 'FILL':
496 c = fillColorCycle[fillCycle]
497 fillCycle = (fillCycle + 1) % len(fillColorCycle)
500 elif path.pathType == 'WALL-INNER':
503 elif path.pathType == 'SUPPORT':
505 elif path.pathType == 'SKIRT':
508 c = extrudeColor[path.extruder]
509 if path.type == 'retract':
511 if path.type == 'extrude' and not drawQuick:
514 for i in xrange(0, len(path.list) - 1):
516 v1 = path.list[i + 1]
518 # Calculate line width from ePerDistance (needs layer thickness and filament diameter)
519 dist = (v0 - v1).vsize()
520 if dist > 0 and path.layerThickness > 0:
521 extrusionMMperDist = (v1.e - v0.e) / dist
522 lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply
524 drawLength += (v0 - v1).vsize()
525 normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1))
528 vv2 = v0 + normal * lineWidth
529 vv3 = v1 + normal * lineWidth
530 vv0 = v0 - normal * lineWidth
531 vv1 = v1 - normal * lineWidth
535 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
536 glVertex3f(vv1.x, vv1.y, vv1.z - zOffset)
537 glVertex3f(vv3.x, vv3.y, vv3.z - zOffset)
538 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
540 if prevNormal is not None:
541 n = (normal + prevNormal)
543 vv4 = v0 + n * lineWidth
544 vv5 = v0 - n * lineWidth
547 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
548 glVertex3f(vv4.x, vv4.y, vv4.z - zOffset)
549 glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset)
550 glVertex3f(v0.x, v0.y, v0.z - zOffset)
552 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
553 glVertex3f(vv5.x, vv5.y, vv5.z - zOffset)
554 glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset)
555 glVertex3f(v0.x, v0.y, v0.z - zOffset)
562 glBegin(GL_LINE_STRIP)
565 glVertex3f(v.x, v.y, v.z)
567 if not path.type == 'move':
568 prevPathWasRetract = False
569 if path.type == 'retract' and path.list[0].almostEqual(path.list[-1]):
570 prevPathWasRetract = True
571 glEnable(GL_CULL_FACE)