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 return glCreateShader != False
39 class GLShader(GLReferenceCounter):
40 def __init__(self, vertexProgram, fragmentProgram):
41 super(GLShader, self).__init__()
42 self._vertexString = vertexProgram
43 self._fragmentString = fragmentProgram
45 self._vertexProgram = shaders.compileShader(vertexProgram, GL_VERTEX_SHADER)
46 self._fragmentProgram = shaders.compileShader(fragmentProgram, GL_FRAGMENT_SHADER)
47 self._program = shaders.compileProgram(self._vertexProgram, self._fragmentProgram)
48 except RuntimeError, e:
53 if self._program is not None:
54 shaders.glUseProgram(self._program)
57 shaders.glUseProgram(0)
60 if self._program is not None:
61 shaders.glDeleteShader(self._vertexProgram)
62 shaders.glDeleteShader(self._fragmentProgram)
63 glDeleteProgram(self._program)
66 def setUniform(self, name, value):
67 if self._program is not None:
68 if type(value) is float:
69 glUniform1f(glGetUniformLocation(self._program, name), value)
70 elif type(value) is numpy.matrix:
71 glUniformMatrix3fv(glGetUniformLocation(self._program, name), 1, False, value.getA().astype(numpy.float32))
73 print 'Unknown type for setUniform: %s' % (str(type(value)))
76 return self._program is not None
78 def getVertexShader(self):
79 return self._vertexString
81 def getFragmentShader(self):
82 return self._fragmentString
85 if self._program is not None and bool(glDeleteProgram):
86 print "Shader was not properly released!"
88 #A Class that acts as an OpenGL shader, but in reality is not none.
89 class GLFakeShader(GLReferenceCounter):
91 super(GLFakeShader, self).__init__()
96 glEnable(GL_COLOR_MATERIAL)
97 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1,1,1,1])
98 glLightfv(GL_LIGHT0, GL_AMBIENT, [0,0,0,0])
99 glLightfv(GL_LIGHT0, GL_SPECULAR, [0,0,0,0])
102 glDisable(GL_LIGHTING)
107 def setUniform(self, name, value):
113 def getVertexShader(self):
116 def getFragmentShader(self):
119 class GLVBO(GLReferenceCounter):
120 def __init__(self, vertexArray, normalArray = None):
121 super(GLVBO, self).__init__()
122 self._buffer = glGenBuffers(1)
123 self._size = len(vertexArray)
124 self._hasNormals = normalArray is not None
125 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
127 glBufferData(GL_ARRAY_BUFFER, numpy.concatenate((vertexArray, normalArray), 1), GL_STATIC_DRAW)
129 glBufferData(GL_ARRAY_BUFFER, vertexArray, GL_STATIC_DRAW)
130 glBindBuffer(GL_ARRAY_BUFFER, 0)
132 def render(self, render_type = GL_TRIANGLES):
133 glEnableClientState(GL_VERTEX_ARRAY)
134 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
137 glEnableClientState(GL_NORMAL_ARRAY)
138 glVertexPointer(3, GL_FLOAT, 2*3*4, c_void_p(0))
139 glNormalPointer(GL_FLOAT, 2*3*4, c_void_p(3 * 4))
141 glVertexPointer(3, GL_FLOAT, 3*4, c_void_p(0))
143 batchSize = 996 #Warning, batchSize needs to be dividable by 4, 3 and 2
144 extraStartPos = int(self._size / batchSize) * batchSize
145 extraCount = self._size - extraStartPos
147 for i in xrange(0, int(self._size / batchSize)):
148 glDrawArrays(render_type, i * batchSize, batchSize)
149 glDrawArrays(render_type, extraStartPos, extraCount)
150 glBindBuffer(GL_ARRAY_BUFFER, 0)
152 glDisableClientState(GL_VERTEX_ARRAY)
154 glDisableClientState(GL_NORMAL_ARRAY)
157 if self._buffer is not None:
158 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
159 glBufferData(GL_ARRAY_BUFFER, None, GL_STATIC_DRAW)
160 glBindBuffer(GL_ARRAY_BUFFER, 0)
161 glDeleteBuffers(1, [self._buffer])
165 if self._buffer is not None and bool(glDeleteBuffers):
166 print "VBO was not properly released!"
168 def DrawMachine(machineSize):
169 glDisable(GL_LIGHTING)
170 glDisable(GL_CULL_FACE)
172 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
176 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
177 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
182 x1 = max(min(x1, sx), 0)
183 y1 = max(min(y1, sy), 0)
184 x2 = max(min(x2, sx), 0)
185 y2 = max(min(y2, sy), 0)
186 if (x & 1) == (y & 1):
187 glColor4ub(5, 171, 231, 127)
189 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
191 glVertex3f(x1, y1, -0.02)
192 glVertex3f(x2, y1, -0.02)
193 glVertex3f(x2, y2, -0.02)
194 glVertex3f(x1, y2, -0.02)
197 glEnable(GL_CULL_FACE)
199 if profile.getPreference('machine_type') == 'ultimaker':
201 glEnable(GL_LIGHTING)
202 glTranslate(100, 200, -1)
203 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8])
204 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5])
206 glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR)
209 if platformMesh is None:
211 platformMesh = meshLoader.loadMesh(getPathForMesh('ultimaker_platform.stl'))
216 DrawMesh(platformMesh)
218 glDisable(GL_LIGHTING)
219 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
221 glColor4ub(5, 171, 231, 64)
223 glVertex3f(0, 0, machineSize.z)
224 glVertex3f(0, machineSize.y, machineSize.z)
225 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
226 glVertex3f(machineSize.x, 0, machineSize.z)
229 glColor4ub(5, 171, 231, 96)
232 glVertex3f(0, 0, machineSize.z)
233 glVertex3f(machineSize.x, 0, machineSize.z)
234 glVertex3f(machineSize.x, 0, 0)
236 glVertex3f(0, machineSize.y, machineSize.z)
237 glVertex3f(0, machineSize.y, 0)
238 glVertex3f(machineSize.x, machineSize.y, 0)
239 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
242 glColor4ub(5, 171, 231, 128)
244 glVertex3f(0, 0, machineSize.z)
246 glVertex3f(0, machineSize.y, 0)
247 glVertex3f(0, machineSize.y, machineSize.z)
249 glVertex3f(machineSize.x, 0, 0)
250 glVertex3f(machineSize.x, 0, machineSize.z)
251 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
252 glVertex3f(machineSize.x, machineSize.y, 0)
257 #Draw the X/Y/Z indicator
277 glDisable(GL_DEPTH_TEST)
281 glTranslate(20, 0, 0)
282 noZ = ResetMatrixRotationAndScale()
283 glDrawStringCenter("X")
289 glTranslate(0, 20, 0)
290 glDrawStringCenter("Y")
297 glTranslate(0, 0, 20)
298 glDrawStringCenter("Z")
302 glEnable(GL_DEPTH_TEST)
304 def glDrawStringCenter(s):
306 glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
308 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
310 def glGetStringSize(s):
313 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
317 def glDrawStringLeft(s):
323 glTranslate(0, 18 * n, 0)
328 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
330 def glDrawStringRight(s):
332 glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
334 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
336 def glDrawQuad(x, y, w, h):
338 glTranslatef(x, y, 0)
339 glDisable(GL_TEXTURE_2D)
348 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
349 tx = float(texID % 4) / 4
350 ty = float(int(texID / 4)) / 8
360 glTranslatef(x, y, 0)
361 glEnable(GL_TEXTURE_2D)
363 glTexCoord2f(tx+tsx, ty)
367 glTexCoord2f(tx, ty+tsy)
369 glTexCoord2f(tx+tsx, ty+tsy)
374 def glDrawStretchedQuad(x, y, w, h, cornerSize, texID):
375 tx0 = float(texID % 4) / 4
376 ty0 = float(int(texID / 4)) / 8
377 tx1 = tx0 + 0.25 / 2.0
378 ty1 = ty0 + 0.125 / 2.0
383 glTranslatef(x, y, 0)
384 glEnable(GL_TEXTURE_2D)
387 glTexCoord2f(tx1, ty0)
388 glVertex2f( cornerSize, 0)
389 glTexCoord2f(tx0, ty0)
391 glTexCoord2f(tx0, ty1)
392 glVertex2f( 0, cornerSize)
393 glTexCoord2f(tx1, ty1)
394 glVertex2f( cornerSize, cornerSize)
396 glTexCoord2f(tx2, ty0)
398 glTexCoord2f(tx1, ty0)
399 glVertex2f( w - cornerSize, 0)
400 glTexCoord2f(tx1, ty1)
401 glVertex2f( w - cornerSize, cornerSize)
402 glTexCoord2f(tx2, ty1)
403 glVertex2f( w, cornerSize)
405 glTexCoord2f(tx1, ty1)
406 glVertex2f( cornerSize, h - cornerSize)
407 glTexCoord2f(tx0, ty1)
408 glVertex2f( 0, h - cornerSize)
409 glTexCoord2f(tx0, ty2)
411 glTexCoord2f(tx1, ty2)
412 glVertex2f( cornerSize, h)
414 glTexCoord2f(tx2, ty1)
415 glVertex2f( w, h - cornerSize)
416 glTexCoord2f(tx1, ty1)
417 glVertex2f( w - cornerSize, h - cornerSize)
418 glTexCoord2f(tx1, ty2)
419 glVertex2f( w - cornerSize, h)
420 glTexCoord2f(tx2, ty2)
424 glTexCoord2f(tx1, ty1)
425 glVertex2f( w-cornerSize, cornerSize)
426 glTexCoord2f(tx1, ty1)
427 glVertex2f( cornerSize, cornerSize)
428 glTexCoord2f(tx1, ty1)
429 glVertex2f( cornerSize, h-cornerSize)
430 glTexCoord2f(tx1, ty1)
431 glVertex2f( w-cornerSize, h-cornerSize)
434 glTexCoord2f(tx2, ty1)
435 glVertex2f( w, cornerSize)
436 glTexCoord2f(tx1, ty1)
437 glVertex2f( w-cornerSize, cornerSize)
438 glTexCoord2f(tx1, ty1)
439 glVertex2f( w-cornerSize, h-cornerSize)
440 glTexCoord2f(tx2, ty1)
441 glVertex2f( w, h-cornerSize)
444 glTexCoord2f(tx1, ty1)
445 glVertex2f( cornerSize, cornerSize)
446 glTexCoord2f(tx0, ty1)
447 glVertex2f( 0, cornerSize)
448 glTexCoord2f(tx0, ty1)
449 glVertex2f( 0, h-cornerSize)
450 glTexCoord2f(tx1, ty1)
451 glVertex2f( cornerSize, h-cornerSize)
454 glTexCoord2f(tx1, ty0)
455 glVertex2f( w-cornerSize, 0)
456 glTexCoord2f(tx1, ty0)
457 glVertex2f( cornerSize, 0)
458 glTexCoord2f(tx1, ty1)
459 glVertex2f( cornerSize, cornerSize)
460 glTexCoord2f(tx1, ty1)
461 glVertex2f( w-cornerSize, cornerSize)
464 glTexCoord2f(tx1, ty1)
465 glVertex2f( w-cornerSize, h-cornerSize)
466 glTexCoord2f(tx1, ty1)
467 glVertex2f( cornerSize, h-cornerSize)
468 glTexCoord2f(tx1, ty2)
469 glVertex2f( cornerSize, h)
470 glTexCoord2f(tx1, ty2)
471 glVertex2f( w-cornerSize, h)
474 glDisable(GL_TEXTURE_2D)
477 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
478 npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
479 npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
480 finalMatrix = npModelMatrix * npProjMatrix
481 finalMatrix = numpy.linalg.inv(finalMatrix)
483 viewport = map(float, viewport)
484 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))
485 vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
486 ret = list(vector)[0:3] / vector[3]
489 def convert3x3MatrixTo4x4(matrix):
490 return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
492 def loadGLTexture(filename):
493 tex = glGenTextures(1)
494 glBindTexture(GL_TEXTURE_2D, tex)
495 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
496 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
497 img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
498 rgbData = img.GetData()
499 alphaData = img.GetAlphaData()
500 if alphaData is not None:
502 for i in xrange(0, len(alphaData)):
503 data += rgbData[i*3:i*3+3] + alphaData[i]
504 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
506 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
509 def ResetMatrixRotationAndScale():
510 matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
514 scale2D = matrix[0][0]
525 if matrix[3][2] != 0.0:
526 matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100)
527 matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100)
530 matrix[0][0] = scale2D
531 matrix[1][1] = scale2D
532 matrix[2][2] = scale2D
536 glLoadMatrixf(matrix)
540 def DrawBox(vMin, vMax):
541 glBegin(GL_LINE_LOOP)
542 glVertex3f(vMin[0], vMin[1], vMin[2])
543 glVertex3f(vMax[0], vMin[1], vMin[2])
544 glVertex3f(vMax[0], vMax[1], vMin[2])
545 glVertex3f(vMin[0], vMax[1], vMin[2])
548 glBegin(GL_LINE_LOOP)
549 glVertex3f(vMin[0], vMin[1], vMax[2])
550 glVertex3f(vMax[0], vMin[1], vMax[2])
551 glVertex3f(vMax[0], vMax[1], vMax[2])
552 glVertex3f(vMin[0], vMax[1], vMax[2])
555 glVertex3f(vMin[0], vMin[1], vMin[2])
556 glVertex3f(vMin[0], vMin[1], vMax[2])
557 glVertex3f(vMax[0], vMin[1], vMin[2])
558 glVertex3f(vMax[0], vMin[1], vMax[2])
559 glVertex3f(vMax[0], vMax[1], vMin[2])
560 glVertex3f(vMax[0], vMax[1], vMax[2])
561 glVertex3f(vMin[0], vMax[1], vMin[2])
562 glVertex3f(vMin[0], vMax[1], vMax[2])
566 def DrawMeshOutline(mesh):
567 glEnable(GL_CULL_FACE)
568 glEnableClientState(GL_VERTEX_ARRAY);
569 glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
573 glPolygonMode(GL_BACK, GL_LINE)
574 glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount)
575 glPolygonMode(GL_BACK, GL_FILL)
578 glDisableClientState(GL_VERTEX_ARRAY)
581 def DrawMesh(mesh, insideOut = False):
582 glEnable(GL_CULL_FACE)
583 glEnableClientState(GL_VERTEX_ARRAY)
584 glEnableClientState(GL_NORMAL_ARRAY)
585 for m in mesh._meshList:
586 glVertexPointer(3, GL_FLOAT, 0, m.vertexes)
588 glNormalPointer(GL_FLOAT, 0, m.invNormal)
590 glNormalPointer(GL_FLOAT, 0, m.normal)
592 #Odd, drawing in batchs is a LOT faster then drawing it all at once.
593 batchSize = 999 #Warning, batchSize needs to be dividable by 3
594 extraStartPos = int(m.vertexCount / batchSize) * batchSize
595 extraCount = m.vertexCount - extraStartPos
598 for i in xrange(0, int(m.vertexCount / batchSize)):
599 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
600 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
604 glNormalPointer(GL_FLOAT, 0, m.normal)
606 glNormalPointer(GL_FLOAT, 0, m.invNormal)
607 for i in xrange(0, int(m.vertexCount / batchSize)):
608 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
609 extraStartPos = int(m.vertexCount / batchSize) * batchSize
610 extraCount = m.vertexCount - extraStartPos
611 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
614 glDisableClientState(GL_VERTEX_ARRAY)
615 glDisableClientState(GL_NORMAL_ARRAY)
618 def DrawMeshSteep(mesh, matrix, angle):
619 cosAngle = math.sin(angle / 180.0 * math.pi)
620 glDisable(GL_LIGHTING)
621 glDepthFunc(GL_EQUAL)
622 normals = (numpy.matrix(mesh.normal, copy = False) * matrix).getA()
623 for i in xrange(0, int(mesh.vertexCount), 3):
624 if normals[i][2] < -0.999999:
625 if mesh.vertexes[i + 0][2] > 0.01:
627 glBegin(GL_TRIANGLES)
628 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
629 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
630 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
632 elif normals[i][2] < -cosAngle:
633 glColor3f(-normals[i][2], 0, 0)
634 glBegin(GL_TRIANGLES)
635 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
636 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
637 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
639 elif normals[i][2] > 0.999999:
640 if mesh.vertexes[i + 0][2] > 0.01:
642 glBegin(GL_TRIANGLES)
643 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
644 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
645 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
647 elif normals[i][2] > cosAngle:
648 glColor3f(normals[i][2], 0, 0)
649 glBegin(GL_TRIANGLES)
650 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
651 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
652 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
656 def DrawGCodeLayer(layer, drawQuick = True):
657 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
658 filamentArea = math.pi * filamentRadius * filamentRadius
659 lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
662 fillColorCycle = [[0.5, 0.5, 0.0, 1], [0.0, 0.5, 0.5, 1], [0.5, 0.0, 0.5, 1]]
663 moveColor = [0, 0, 1, 0.5]
664 retractColor = [1, 0, 0.5, 0.5]
665 supportColor = [0, 1, 1, 1]
666 extrudeColor = [[1, 0, 0, 1], [0, 1, 1, 1], [1, 1, 0, 1], [1, 0, 1, 1]]
667 innerWallColor = [0, 1, 0, 1]
668 skirtColor = [0, 0.5, 0.5, 1]
669 prevPathWasRetract = False
671 glDisable(GL_CULL_FACE)
673 if path.type == 'move':
674 if prevPathWasRetract:
681 if path.type == 'extrude':
682 if path.pathType == 'FILL':
683 c = fillColorCycle[fillCycle]
684 fillCycle = (fillCycle + 1) % len(fillColorCycle)
685 elif path.pathType == 'WALL-INNER':
688 elif path.pathType == 'SUPPORT':
690 elif path.pathType == 'SKIRT':
693 c = extrudeColor[path.extruder]
694 if path.type == 'retract':
696 if path.type == 'extrude' and not drawQuick:
699 for i in xrange(0, len(path.points) - 1):
701 v1 = path.points[i + 1]
703 # Calculate line width from ePerDistance (needs layer thickness and filament diameter)
704 dist = (v0 - v1).vsize()
705 if dist > 0 and path.layerThickness > 0:
706 extrusionMMperDist = (v1.e - v0.e) / dist
707 lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply
709 drawLength += (v0 - v1).vsize()
710 normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1))
713 vv2 = v0 + normal * lineWidth
714 vv3 = v1 + normal * lineWidth
715 vv0 = v0 - normal * lineWidth
716 vv1 = v1 - normal * lineWidth
720 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
721 glVertex3f(vv1.x, vv1.y, vv1.z - zOffset)
722 glVertex3f(vv3.x, vv3.y, vv3.z - zOffset)
723 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
725 if prevNormal is not None:
726 n = (normal + prevNormal)
728 vv4 = v0 + n * lineWidth
729 vv5 = v0 - n * lineWidth
732 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
733 glVertex3f(vv4.x, vv4.y, vv4.z - zOffset)
734 glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset)
735 glVertex3f(v0.x, v0.y, v0.z - zOffset)
737 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
738 glVertex3f(vv5.x, vv5.y, vv5.z - zOffset)
739 glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset)
740 glVertex3f(v0.x, v0.y, v0.z - zOffset)
748 glBegin(GL_TRIANGLES)
749 for v in path.points:
750 glVertex3f(v[0], v[1], v[2])
753 if not path.type == 'move':
754 prevPathWasRetract = False
755 #if path.type == 'retract' and path.points[0].almostEqual(path.points[-1]):
756 # prevPathWasRetract = True
757 glEnable(GL_CULL_FACE)