1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
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 GLReferenceCounter(object):
34 return self._refCounter <= 0
36 def hasShaderSupport():
37 if bool(glCreateShader):
41 class GLShader(GLReferenceCounter):
42 def __init__(self, vertexProgram, fragmentProgram):
43 super(GLShader, self).__init__()
44 self._vertexString = vertexProgram
45 self._fragmentString = fragmentProgram
47 vertexShader = shaders.compileShader(vertexProgram, GL_VERTEX_SHADER)
48 fragmentShader = shaders.compileShader(fragmentProgram, GL_FRAGMENT_SHADER)
50 #shader.compileProgram tries to return the shader program as a overloaded int. But the return value of a shader does not always fit in a int (needs to be a long). So we do raw OpenGL calls.
51 # self._program = shaders.compileProgram(self._vertexProgram, self._fragmentProgram)
52 self._program = glCreateProgram()
53 glAttachShader(self._program, vertexShader)
54 glAttachShader(self._program, fragmentShader)
55 glLinkProgram(self._program)
56 # Validation has to occur *after* linking
57 glValidateProgram(self._program)
58 if glGetProgramiv(self._program, GL_VALIDATE_STATUS) == GL_FALSE:
59 raise RuntimeError("Validation failure: %s"%(glGetProgramInfoLog(self._program)))
60 if glGetProgramiv(self._program, GL_LINK_STATUS) == GL_FALSE:
61 raise RuntimeError("Link failure: %s" % (glGetProgramInfoLog(self._program)))
62 glDeleteShader(vertexShader)
63 glDeleteShader(fragmentShader)
64 except RuntimeError, e:
69 if self._program is not None:
70 shaders.glUseProgram(self._program)
73 shaders.glUseProgram(0)
76 if self._program is not None:
77 glDeleteProgram(self._program)
80 def setUniform(self, name, value):
81 if self._program is not None:
82 if type(value) is float:
83 glUniform1f(glGetUniformLocation(self._program, name), value)
84 elif type(value) is numpy.matrix:
85 glUniformMatrix3fv(glGetUniformLocation(self._program, name), 1, False, value.getA().astype(numpy.float32))
87 print 'Unknown type for setUniform: %s' % (str(type(value)))
90 return self._program is not None
92 def getVertexShader(self):
93 return self._vertexString
95 def getFragmentShader(self):
96 return self._fragmentString
99 if self._program is not None and bool(glDeleteProgram):
100 print "Shader was not properly released!"
102 #A Class that acts as an OpenGL shader, but in reality is not none.
103 class GLFakeShader(GLReferenceCounter):
105 super(GLFakeShader, self).__init__()
108 glEnable(GL_LIGHTING)
110 glEnable(GL_COLOR_MATERIAL)
111 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1,1,1,1])
112 glLightfv(GL_LIGHT0, GL_AMBIENT, [0,0,0,0])
113 glLightfv(GL_LIGHT0, GL_SPECULAR, [0,0,0,0])
116 glDisable(GL_LIGHTING)
121 def setUniform(self, name, value):
127 def getVertexShader(self):
130 def getFragmentShader(self):
133 class GLVBO(GLReferenceCounter):
134 def __init__(self, vertexArray, normalArray = None):
135 super(GLVBO, self).__init__()
136 if not bool(glGenBuffers):
137 self._vertexArray = vertexArray
138 self._normalArray = normalArray
139 self._size = len(vertexArray)
141 self._hasNormals = self._normalArray is not None
143 self._buffer = glGenBuffers(1)
144 self._size = len(vertexArray)
145 self._hasNormals = normalArray is not None
146 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
148 glBufferData(GL_ARRAY_BUFFER, numpy.concatenate((vertexArray, normalArray), 1), GL_STATIC_DRAW)
150 glBufferData(GL_ARRAY_BUFFER, vertexArray, GL_STATIC_DRAW)
151 glBindBuffer(GL_ARRAY_BUFFER, 0)
153 def render(self, render_type = GL_TRIANGLES):
154 glEnableClientState(GL_VERTEX_ARRAY)
155 if self._buffer is None:
156 glVertexPointer(3, GL_FLOAT, 0, self._vertexArray)
158 glEnableClientState(GL_NORMAL_ARRAY)
159 glNormalPointer(GL_FLOAT, 0, self._normalArray)
161 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
163 glEnableClientState(GL_NORMAL_ARRAY)
164 glVertexPointer(3, GL_FLOAT, 2*3*4, c_void_p(0))
165 glNormalPointer(GL_FLOAT, 2*3*4, c_void_p(3 * 4))
167 glVertexPointer(3, GL_FLOAT, 3*4, c_void_p(0))
169 batchSize = 996 #Warning, batchSize needs to be dividable by 4, 3 and 2
170 extraStartPos = int(self._size / batchSize) * batchSize
171 extraCount = self._size - extraStartPos
173 for i in xrange(0, int(self._size / batchSize)):
174 glDrawArrays(render_type, i * batchSize, batchSize)
175 glDrawArrays(render_type, extraStartPos, extraCount)
176 if self._buffer is not None:
177 glBindBuffer(GL_ARRAY_BUFFER, 0)
179 glDisableClientState(GL_VERTEX_ARRAY)
181 glDisableClientState(GL_NORMAL_ARRAY)
184 if self._buffer is not None:
185 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
186 glBufferData(GL_ARRAY_BUFFER, None, GL_STATIC_DRAW)
187 glBindBuffer(GL_ARRAY_BUFFER, 0)
188 glDeleteBuffers(1, [self._buffer])
190 self._vertexArray = None
191 self._normalArray = None
194 if self._buffer is not None and bool(glDeleteBuffers):
195 print "VBO was not properly released!"
197 def glDrawStringCenter(s):
199 glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
201 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
203 def glGetStringSize(s):
206 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
210 def glDrawStringLeft(s):
216 glTranslate(0, 18 * n, 0)
221 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
223 def glDrawStringRight(s):
225 glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
227 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
229 def glDrawQuad(x, y, w, h):
231 glTranslatef(x, y, 0)
232 glDisable(GL_TEXTURE_2D)
241 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
242 tx = float(texID % 4) / 4
243 ty = float(int(texID / 4)) / 8
253 glTranslatef(x, y, 0)
254 glEnable(GL_TEXTURE_2D)
256 glTexCoord2f(tx+tsx, ty)
260 glTexCoord2f(tx, ty+tsy)
262 glTexCoord2f(tx+tsx, ty+tsy)
267 def glDrawStretchedQuad(x, y, w, h, cornerSize, texID):
268 tx0 = float(texID % 4) / 4
269 ty0 = float(int(texID / 4)) / 8
270 tx1 = tx0 + 0.25 / 2.0
271 ty1 = ty0 + 0.125 / 2.0
276 glTranslatef(x, y, 0)
277 glEnable(GL_TEXTURE_2D)
280 glTexCoord2f(tx1, ty0)
281 glVertex2f( cornerSize, 0)
282 glTexCoord2f(tx0, ty0)
284 glTexCoord2f(tx0, ty1)
285 glVertex2f( 0, cornerSize)
286 glTexCoord2f(tx1, ty1)
287 glVertex2f( cornerSize, cornerSize)
289 glTexCoord2f(tx2, ty0)
291 glTexCoord2f(tx1, ty0)
292 glVertex2f( w - cornerSize, 0)
293 glTexCoord2f(tx1, ty1)
294 glVertex2f( w - cornerSize, cornerSize)
295 glTexCoord2f(tx2, ty1)
296 glVertex2f( w, cornerSize)
298 glTexCoord2f(tx1, ty1)
299 glVertex2f( cornerSize, h - cornerSize)
300 glTexCoord2f(tx0, ty1)
301 glVertex2f( 0, h - cornerSize)
302 glTexCoord2f(tx0, ty2)
304 glTexCoord2f(tx1, ty2)
305 glVertex2f( cornerSize, h)
307 glTexCoord2f(tx2, ty1)
308 glVertex2f( w, h - cornerSize)
309 glTexCoord2f(tx1, ty1)
310 glVertex2f( w - cornerSize, h - cornerSize)
311 glTexCoord2f(tx1, ty2)
312 glVertex2f( w - cornerSize, h)
313 glTexCoord2f(tx2, ty2)
317 glTexCoord2f(tx1, ty1)
318 glVertex2f( w-cornerSize, cornerSize)
319 glTexCoord2f(tx1, ty1)
320 glVertex2f( cornerSize, cornerSize)
321 glTexCoord2f(tx1, ty1)
322 glVertex2f( cornerSize, h-cornerSize)
323 glTexCoord2f(tx1, ty1)
324 glVertex2f( w-cornerSize, h-cornerSize)
327 glTexCoord2f(tx2, ty1)
328 glVertex2f( w, cornerSize)
329 glTexCoord2f(tx1, ty1)
330 glVertex2f( w-cornerSize, cornerSize)
331 glTexCoord2f(tx1, ty1)
332 glVertex2f( w-cornerSize, h-cornerSize)
333 glTexCoord2f(tx2, ty1)
334 glVertex2f( w, h-cornerSize)
337 glTexCoord2f(tx1, ty1)
338 glVertex2f( cornerSize, cornerSize)
339 glTexCoord2f(tx0, ty1)
340 glVertex2f( 0, cornerSize)
341 glTexCoord2f(tx0, ty1)
342 glVertex2f( 0, h-cornerSize)
343 glTexCoord2f(tx1, ty1)
344 glVertex2f( cornerSize, h-cornerSize)
347 glTexCoord2f(tx1, ty0)
348 glVertex2f( w-cornerSize, 0)
349 glTexCoord2f(tx1, ty0)
350 glVertex2f( cornerSize, 0)
351 glTexCoord2f(tx1, ty1)
352 glVertex2f( cornerSize, cornerSize)
353 glTexCoord2f(tx1, ty1)
354 glVertex2f( w-cornerSize, cornerSize)
357 glTexCoord2f(tx1, ty1)
358 glVertex2f( w-cornerSize, h-cornerSize)
359 glTexCoord2f(tx1, ty1)
360 glVertex2f( cornerSize, h-cornerSize)
361 glTexCoord2f(tx1, ty2)
362 glVertex2f( cornerSize, h)
363 glTexCoord2f(tx1, ty2)
364 glVertex2f( w-cornerSize, h)
367 glDisable(GL_TEXTURE_2D)
370 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
371 npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
372 npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
373 finalMatrix = npModelMatrix * npProjMatrix
374 finalMatrix = numpy.linalg.inv(finalMatrix)
376 viewport = map(float, viewport)
377 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))
378 vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
379 ret = list(vector)[0:3] / vector[3]
382 def convert3x3MatrixTo4x4(matrix):
383 return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
385 def loadGLTexture(filename):
386 tex = glGenTextures(1)
387 glBindTexture(GL_TEXTURE_2D, tex)
388 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
389 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
390 img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
391 rgbData = img.GetData()
392 alphaData = img.GetAlphaData()
393 if alphaData is not None:
395 for i in xrange(0, len(alphaData)):
396 data += rgbData[i*3:i*3+3] + alphaData[i]
397 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
399 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
402 def ResetMatrixRotationAndScale():
403 matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
407 scale2D = matrix[0][0]
418 if matrix[3][2] != 0.0:
419 matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100)
420 matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100)
423 matrix[0][0] = scale2D
424 matrix[1][1] = scale2D
425 matrix[2][2] = scale2D
429 glLoadMatrixf(matrix)
433 def DrawBox(vMin, vMax):
434 glBegin(GL_LINE_LOOP)
435 glVertex3f(vMin[0], vMin[1], vMin[2])
436 glVertex3f(vMax[0], vMin[1], vMin[2])
437 glVertex3f(vMax[0], vMax[1], vMin[2])
438 glVertex3f(vMin[0], vMax[1], vMin[2])
441 glBegin(GL_LINE_LOOP)
442 glVertex3f(vMin[0], vMin[1], vMax[2])
443 glVertex3f(vMax[0], vMin[1], vMax[2])
444 glVertex3f(vMax[0], vMax[1], vMax[2])
445 glVertex3f(vMin[0], vMax[1], vMax[2])
448 glVertex3f(vMin[0], vMin[1], vMin[2])
449 glVertex3f(vMin[0], vMin[1], vMax[2])
450 glVertex3f(vMax[0], vMin[1], vMin[2])
451 glVertex3f(vMax[0], vMin[1], vMax[2])
452 glVertex3f(vMax[0], vMax[1], vMin[2])
453 glVertex3f(vMax[0], vMax[1], vMax[2])
454 glVertex3f(vMin[0], vMax[1], vMin[2])
455 glVertex3f(vMin[0], vMax[1], vMax[2])
459 def DrawMeshOutline(mesh):
460 glEnable(GL_CULL_FACE)
461 glEnableClientState(GL_VERTEX_ARRAY);
462 glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
466 glPolygonMode(GL_BACK, GL_LINE)
467 glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount)
468 glPolygonMode(GL_BACK, GL_FILL)
471 glDisableClientState(GL_VERTEX_ARRAY)
474 def DrawMesh(mesh, insideOut = False):
475 glEnable(GL_CULL_FACE)
476 glEnableClientState(GL_VERTEX_ARRAY)
477 glEnableClientState(GL_NORMAL_ARRAY)
478 for m in mesh._meshList:
479 glVertexPointer(3, GL_FLOAT, 0, m.vertexes)
481 glNormalPointer(GL_FLOAT, 0, m.invNormal)
483 glNormalPointer(GL_FLOAT, 0, m.normal)
485 #Odd, drawing in batchs is a LOT faster then drawing it all at once.
486 batchSize = 999 #Warning, batchSize needs to be dividable by 3
487 extraStartPos = int(m.vertexCount / batchSize) * batchSize
488 extraCount = m.vertexCount - extraStartPos
491 for i in xrange(0, int(m.vertexCount / batchSize)):
492 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
493 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
497 glNormalPointer(GL_FLOAT, 0, m.normal)
499 glNormalPointer(GL_FLOAT, 0, m.invNormal)
500 for i in xrange(0, int(m.vertexCount / batchSize)):
501 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
502 extraStartPos = int(m.vertexCount / batchSize) * batchSize
503 extraCount = m.vertexCount - extraStartPos
504 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
507 glDisableClientState(GL_VERTEX_ARRAY)
508 glDisableClientState(GL_NORMAL_ARRAY)
511 def DrawMeshSteep(mesh, matrix, angle):
512 cosAngle = math.sin(angle / 180.0 * math.pi)
513 glDisable(GL_LIGHTING)
514 glDepthFunc(GL_EQUAL)
515 normals = (numpy.matrix(mesh.normal, copy = False) * matrix).getA()
516 for i in xrange(0, int(mesh.vertexCount), 3):
517 if normals[i][2] < -0.999999:
518 if mesh.vertexes[i + 0][2] > 0.01:
520 glBegin(GL_TRIANGLES)
521 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
522 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
523 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
525 elif normals[i][2] < -cosAngle:
526 glColor3f(-normals[i][2], 0, 0)
527 glBegin(GL_TRIANGLES)
528 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
529 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
530 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
532 elif normals[i][2] > 0.999999:
533 if mesh.vertexes[i + 0][2] > 0.01:
535 glBegin(GL_TRIANGLES)
536 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
537 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
538 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
540 elif normals[i][2] > cosAngle:
541 glColor3f(normals[i][2], 0, 0)
542 glBegin(GL_TRIANGLES)
543 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
544 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
545 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
549 def DrawGCodeLayer(layer, drawQuick = True):
550 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
551 filamentArea = math.pi * filamentRadius * filamentRadius
552 lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
555 fillColorCycle = [[0.5, 0.5, 0.0, 1], [0.0, 0.5, 0.5, 1], [0.5, 0.0, 0.5, 1]]
556 moveColor = [0, 0, 1, 0.5]
557 retractColor = [1, 0, 0.5, 0.5]
558 supportColor = [0, 1, 1, 1]
559 extrudeColor = [[1, 0, 0, 1], [0, 1, 1, 1], [1, 1, 0, 1], [1, 0, 1, 1]]
560 innerWallColor = [0, 1, 0, 1]
561 skirtColor = [0, 0.5, 0.5, 1]
562 prevPathWasRetract = False
564 glDisable(GL_CULL_FACE)
566 if path.type == 'move':
567 if prevPathWasRetract:
574 if path.type == 'extrude':
575 if path.pathType == 'FILL':
576 c = fillColorCycle[fillCycle]
577 fillCycle = (fillCycle + 1) % len(fillColorCycle)
578 elif path.pathType == 'WALL-INNER':
581 elif path.pathType == 'SUPPORT':
583 elif path.pathType == 'SKIRT':
586 c = extrudeColor[path.extruder]
587 if path.type == 'retract':
589 if path.type == 'extrude' and not drawQuick:
592 for i in xrange(0, len(path.points) - 1):
594 v1 = path.points[i + 1]
596 # Calculate line width from ePerDistance (needs layer thickness and filament diameter)
597 dist = (v0 - v1).vsize()
598 if dist > 0 and path.layerThickness > 0:
599 extrusionMMperDist = (v1.e - v0.e) / dist
600 lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply
602 drawLength += (v0 - v1).vsize()
603 normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1))
606 vv2 = v0 + normal * lineWidth
607 vv3 = v1 + normal * lineWidth
608 vv0 = v0 - normal * lineWidth
609 vv1 = v1 - normal * lineWidth
613 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
614 glVertex3f(vv1.x, vv1.y, vv1.z - zOffset)
615 glVertex3f(vv3.x, vv3.y, vv3.z - zOffset)
616 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
618 if prevNormal is not None:
619 n = (normal + prevNormal)
621 vv4 = v0 + n * lineWidth
622 vv5 = v0 - n * lineWidth
625 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
626 glVertex3f(vv4.x, vv4.y, vv4.z - zOffset)
627 glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset)
628 glVertex3f(v0.x, v0.y, v0.z - zOffset)
630 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
631 glVertex3f(vv5.x, vv5.y, vv5.z - zOffset)
632 glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset)
633 glVertex3f(v0.x, v0.y, v0.z - zOffset)
641 glBegin(GL_TRIANGLES)
642 for v in path.points:
643 glVertex3f(v[0], v[1], v[2])
646 if not path.type == 'move':
647 prevPathWasRetract = False
648 #if path.type == 'retract' and path.points[0].almostEqual(path.points[-1]):
649 # prevPathWasRetract = True
650 glEnable(GL_CULL_FACE)