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 class GLShader(GLReferenceCounter):
37 def __init__(self, vertexProgram, fragmentProgram):
38 super(GLShader, self).__init__()
39 self._vertexString = vertexProgram
40 self._fragmentString = fragmentProgram
42 self._vertexProgram = shaders.compileShader(vertexProgram, GL_VERTEX_SHADER)
43 self._fragmentProgram = shaders.compileShader(fragmentProgram, GL_FRAGMENT_SHADER)
44 self._program = shaders.compileProgram(self._vertexProgram, self._fragmentProgram)
45 except RuntimeError, e:
50 if self._program is not None:
51 shaders.glUseProgram(self._program)
54 shaders.glUseProgram(0)
57 if self._program is not None:
58 shaders.glDeleteShader(self._vertexProgram)
59 shaders.glDeleteShader(self._fragmentProgram)
60 glDeleteProgram(self._program)
63 def setUniform(self, name, value):
64 if self._program is not None:
65 if type(value) is float:
66 glUniform1f(glGetUniformLocation(self._program, name), value)
67 elif type(value) is numpy.matrix:
68 glUniformMatrix3fv(glGetUniformLocation(self._program, name), 1, False, value.getA().astype(numpy.float32))
70 print 'Unknown type for setUniform: %s' % (str(type(value)))
73 return self._program is not None
75 def getVertexShader(self):
76 return self._vertexString
78 def getFragmentShader(self):
79 return self._fragmentString
82 if self._program is not None and bool(glDeleteProgram):
83 print "Shader was not properly released!"
85 class GLVBO(GLReferenceCounter):
86 def __init__(self, vertexArray, normalArray = None):
87 super(GLVBO, self).__init__()
88 self._buffer = glGenBuffers(1)
89 self._size = len(vertexArray)
90 self._hasNormals = normalArray is not None
91 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
93 glBufferData(GL_ARRAY_BUFFER, numpy.concatenate((vertexArray, normalArray), 1), GL_STATIC_DRAW)
95 glBufferData(GL_ARRAY_BUFFER, vertexArray, GL_STATIC_DRAW)
96 glBindBuffer(GL_ARRAY_BUFFER, 0)
98 def render(self, render_type = GL_TRIANGLES):
99 glEnableClientState(GL_VERTEX_ARRAY)
100 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
103 glEnableClientState(GL_NORMAL_ARRAY)
104 glVertexPointer(3, GL_FLOAT, 2*3*4, c_void_p(0))
105 glNormalPointer(GL_FLOAT, 2*3*4, c_void_p(3 * 4))
107 glVertexPointer(3, GL_FLOAT, 3*4, c_void_p(0))
109 batchSize = 996 #Warning, batchSize needs to be dividable by 4, 3 and 2
110 extraStartPos = int(self._size / batchSize) * batchSize
111 extraCount = self._size - extraStartPos
113 for i in xrange(0, int(self._size / batchSize)):
114 glDrawArrays(render_type, i * batchSize, batchSize)
115 glDrawArrays(render_type, extraStartPos, extraCount)
116 glBindBuffer(GL_ARRAY_BUFFER, 0)
118 glDisableClientState(GL_VERTEX_ARRAY)
120 glDisableClientState(GL_NORMAL_ARRAY)
123 if self._buffer is not None:
124 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
125 glBufferData(GL_ARRAY_BUFFER, None, GL_STATIC_DRAW)
126 glBindBuffer(GL_ARRAY_BUFFER, 0)
127 glDeleteBuffers(1, [self._buffer])
131 if self._buffer is not None and bool(glDeleteBuffers):
132 print "VBO was not properly released!"
134 def DrawMachine(machineSize):
135 glDisable(GL_LIGHTING)
136 glDisable(GL_CULL_FACE)
138 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
142 for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
143 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
148 x1 = max(min(x1, sx), 0)
149 y1 = max(min(y1, sy), 0)
150 x2 = max(min(x2, sx), 0)
151 y2 = max(min(y2, sy), 0)
152 if (x & 1) == (y & 1):
153 glColor4ub(5, 171, 231, 127)
155 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
157 glVertex3f(x1, y1, -0.02)
158 glVertex3f(x2, y1, -0.02)
159 glVertex3f(x2, y2, -0.02)
160 glVertex3f(x1, y2, -0.02)
163 glEnable(GL_CULL_FACE)
165 if profile.getPreference('machine_type') == 'ultimaker':
167 glEnable(GL_LIGHTING)
168 glTranslate(100, 200, -1)
169 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8])
170 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5])
172 glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR)
175 if platformMesh is None:
177 platformMesh = meshLoader.loadMesh(getPathForMesh('ultimaker_platform.stl'))
182 DrawMesh(platformMesh)
184 glDisable(GL_LIGHTING)
185 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
187 glColor4ub(5, 171, 231, 64)
189 glVertex3f(0, 0, machineSize.z)
190 glVertex3f(0, machineSize.y, machineSize.z)
191 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
192 glVertex3f(machineSize.x, 0, machineSize.z)
195 glColor4ub(5, 171, 231, 96)
198 glVertex3f(0, 0, machineSize.z)
199 glVertex3f(machineSize.x, 0, machineSize.z)
200 glVertex3f(machineSize.x, 0, 0)
202 glVertex3f(0, machineSize.y, machineSize.z)
203 glVertex3f(0, machineSize.y, 0)
204 glVertex3f(machineSize.x, machineSize.y, 0)
205 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
208 glColor4ub(5, 171, 231, 128)
210 glVertex3f(0, 0, machineSize.z)
212 glVertex3f(0, machineSize.y, 0)
213 glVertex3f(0, machineSize.y, machineSize.z)
215 glVertex3f(machineSize.x, 0, 0)
216 glVertex3f(machineSize.x, 0, machineSize.z)
217 glVertex3f(machineSize.x, machineSize.y, machineSize.z)
218 glVertex3f(machineSize.x, machineSize.y, 0)
223 #Draw the X/Y/Z indicator
243 glDisable(GL_DEPTH_TEST)
247 glTranslate(20, 0, 0)
248 noZ = ResetMatrixRotationAndScale()
249 glDrawStringCenter("X")
255 glTranslate(0, 20, 0)
256 glDrawStringCenter("Y")
263 glTranslate(0, 0, 20)
264 glDrawStringCenter("Z")
268 glEnable(GL_DEPTH_TEST)
270 def glDrawStringCenter(s):
272 glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
274 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
276 def glGetStringSize(s):
279 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
283 def glDrawStringLeft(s):
289 glTranslate(0, 18 * n, 0)
294 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
296 def glDrawStringRight(s):
298 glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
300 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
302 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
303 tx = float(texID % 4) / 4
304 ty = float(int(texID / 4)) / 8
314 glTranslatef(x, y, 0)
315 glEnable(GL_TEXTURE_2D)
317 glTexCoord2f(tx+tsx, ty)
321 glTexCoord2f(tx, ty+tsy)
323 glTexCoord2f(tx+tsx, ty+tsy)
328 def glDrawStretchedQuad(x, y, w, h, cornerSize, texID):
329 tx0 = float(texID % 4) / 4
330 ty0 = float(int(texID / 4)) / 8
331 tx1 = tx0 + 0.25 / 2.0
332 ty1 = ty0 + 0.125 / 2.0
337 glTranslatef(x, y, 0)
338 glEnable(GL_TEXTURE_2D)
341 glTexCoord2f(tx1, ty0)
342 glVertex2f( cornerSize, 0)
343 glTexCoord2f(tx0, ty0)
345 glTexCoord2f(tx0, ty1)
346 glVertex2f( 0, cornerSize)
347 glTexCoord2f(tx1, ty1)
348 glVertex2f( cornerSize, cornerSize)
350 glTexCoord2f(tx2, ty0)
352 glTexCoord2f(tx1, ty0)
353 glVertex2f( w - cornerSize, 0)
354 glTexCoord2f(tx1, ty1)
355 glVertex2f( w - cornerSize, cornerSize)
356 glTexCoord2f(tx2, ty1)
357 glVertex2f( w, cornerSize)
359 glTexCoord2f(tx1, ty1)
360 glVertex2f( cornerSize, h - cornerSize)
361 glTexCoord2f(tx0, ty1)
362 glVertex2f( 0, h - cornerSize)
363 glTexCoord2f(tx0, ty2)
365 glTexCoord2f(tx1, ty2)
366 glVertex2f( cornerSize, h)
368 glTexCoord2f(tx2, ty1)
369 glVertex2f( w, h - cornerSize)
370 glTexCoord2f(tx1, ty1)
371 glVertex2f( w - cornerSize, h - cornerSize)
372 glTexCoord2f(tx1, ty2)
373 glVertex2f( w - cornerSize, h)
374 glTexCoord2f(tx2, ty2)
378 glTexCoord2f(tx1, ty1)
379 glVertex2f( w-cornerSize, cornerSize)
380 glTexCoord2f(tx1, ty1)
381 glVertex2f( cornerSize, cornerSize)
382 glTexCoord2f(tx1, ty1)
383 glVertex2f( cornerSize, h-cornerSize)
384 glTexCoord2f(tx1, ty1)
385 glVertex2f( w-cornerSize, h-cornerSize)
388 glTexCoord2f(tx2, ty1)
389 glVertex2f( w, cornerSize)
390 glTexCoord2f(tx1, ty1)
391 glVertex2f( w-cornerSize, cornerSize)
392 glTexCoord2f(tx1, ty1)
393 glVertex2f( w-cornerSize, h-cornerSize)
394 glTexCoord2f(tx2, ty1)
395 glVertex2f( w, h-cornerSize)
398 glTexCoord2f(tx1, ty1)
399 glVertex2f( cornerSize, cornerSize)
400 glTexCoord2f(tx0, ty1)
401 glVertex2f( 0, cornerSize)
402 glTexCoord2f(tx0, ty1)
403 glVertex2f( 0, h-cornerSize)
404 glTexCoord2f(tx1, ty1)
405 glVertex2f( cornerSize, h-cornerSize)
408 glTexCoord2f(tx1, ty0)
409 glVertex2f( w-cornerSize, 0)
410 glTexCoord2f(tx1, ty0)
411 glVertex2f( cornerSize, 0)
412 glTexCoord2f(tx1, ty1)
413 glVertex2f( cornerSize, cornerSize)
414 glTexCoord2f(tx1, ty1)
415 glVertex2f( w-cornerSize, cornerSize)
418 glTexCoord2f(tx1, ty1)
419 glVertex2f( w-cornerSize, h-cornerSize)
420 glTexCoord2f(tx1, ty1)
421 glVertex2f( cornerSize, h-cornerSize)
422 glTexCoord2f(tx1, ty2)
423 glVertex2f( cornerSize, h)
424 glTexCoord2f(tx1, ty2)
425 glVertex2f( w-cornerSize, h)
428 glDisable(GL_TEXTURE_2D)
431 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
432 npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
433 npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
434 finalMatrix = npModelMatrix * npProjMatrix
435 finalMatrix = numpy.linalg.inv(finalMatrix)
437 viewport = map(float, viewport)
438 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))
439 vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
440 ret = list(vector)[0:3] / vector[3]
443 def convert3x3MatrixTo4x4(matrix):
444 return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
446 def loadGLTexture(filename):
447 tex = glGenTextures(1)
448 glBindTexture(GL_TEXTURE_2D, tex)
449 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
450 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
451 img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
452 rgbData = img.GetData()
453 alphaData = img.GetAlphaData()
454 if alphaData is not None:
456 for i in xrange(0, len(alphaData)):
457 data += rgbData[i*3:i*3+3] + alphaData[i]
458 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
460 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
463 def ResetMatrixRotationAndScale():
464 matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
468 scale2D = matrix[0][0]
479 if matrix[3][2] != 0.0:
480 matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100)
481 matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100)
484 matrix[0][0] = scale2D
485 matrix[1][1] = scale2D
486 matrix[2][2] = scale2D
490 glLoadMatrixf(matrix)
494 def DrawBox(vMin, vMax):
495 glBegin(GL_LINE_LOOP)
496 glVertex3f(vMin[0], vMin[1], vMin[2])
497 glVertex3f(vMax[0], vMin[1], vMin[2])
498 glVertex3f(vMax[0], vMax[1], vMin[2])
499 glVertex3f(vMin[0], vMax[1], vMin[2])
502 glBegin(GL_LINE_LOOP)
503 glVertex3f(vMin[0], vMin[1], vMax[2])
504 glVertex3f(vMax[0], vMin[1], vMax[2])
505 glVertex3f(vMax[0], vMax[1], vMax[2])
506 glVertex3f(vMin[0], vMax[1], vMax[2])
509 glVertex3f(vMin[0], vMin[1], vMin[2])
510 glVertex3f(vMin[0], vMin[1], vMax[2])
511 glVertex3f(vMax[0], vMin[1], vMin[2])
512 glVertex3f(vMax[0], vMin[1], vMax[2])
513 glVertex3f(vMax[0], vMax[1], vMin[2])
514 glVertex3f(vMax[0], vMax[1], vMax[2])
515 glVertex3f(vMin[0], vMax[1], vMin[2])
516 glVertex3f(vMin[0], vMax[1], vMax[2])
520 def DrawMeshOutline(mesh):
521 glEnable(GL_CULL_FACE)
522 glEnableClientState(GL_VERTEX_ARRAY);
523 glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
527 glPolygonMode(GL_BACK, GL_LINE)
528 glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount)
529 glPolygonMode(GL_BACK, GL_FILL)
532 glDisableClientState(GL_VERTEX_ARRAY)
535 def DrawMesh(mesh, insideOut = False):
536 glEnable(GL_CULL_FACE)
537 glEnableClientState(GL_VERTEX_ARRAY)
538 glEnableClientState(GL_NORMAL_ARRAY)
539 for m in mesh._meshList:
540 glVertexPointer(3, GL_FLOAT, 0, m.vertexes)
542 glNormalPointer(GL_FLOAT, 0, m.invNormal)
544 glNormalPointer(GL_FLOAT, 0, m.normal)
546 #Odd, drawing in batchs is a LOT faster then drawing it all at once.
547 batchSize = 999 #Warning, batchSize needs to be dividable by 3
548 extraStartPos = int(m.vertexCount / batchSize) * batchSize
549 extraCount = m.vertexCount - extraStartPos
552 for i in xrange(0, int(m.vertexCount / batchSize)):
553 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
554 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
558 glNormalPointer(GL_FLOAT, 0, m.normal)
560 glNormalPointer(GL_FLOAT, 0, m.invNormal)
561 for i in xrange(0, int(m.vertexCount / batchSize)):
562 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
563 extraStartPos = int(m.vertexCount / batchSize) * batchSize
564 extraCount = m.vertexCount - extraStartPos
565 glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
568 glDisableClientState(GL_VERTEX_ARRAY)
569 glDisableClientState(GL_NORMAL_ARRAY)
572 def DrawMeshSteep(mesh, matrix, angle):
573 cosAngle = math.sin(angle / 180.0 * math.pi)
574 glDisable(GL_LIGHTING)
575 glDepthFunc(GL_EQUAL)
576 normals = (numpy.matrix(mesh.normal, copy = False) * matrix).getA()
577 for i in xrange(0, int(mesh.vertexCount), 3):
578 if normals[i][2] < -0.999999:
579 if mesh.vertexes[i + 0][2] > 0.01:
581 glBegin(GL_TRIANGLES)
582 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
583 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
584 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
586 elif normals[i][2] < -cosAngle:
587 glColor3f(-normals[i][2], 0, 0)
588 glBegin(GL_TRIANGLES)
589 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
590 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
591 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
593 elif normals[i][2] > 0.999999:
594 if mesh.vertexes[i + 0][2] > 0.01:
596 glBegin(GL_TRIANGLES)
597 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
598 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
599 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
601 elif normals[i][2] > cosAngle:
602 glColor3f(normals[i][2], 0, 0)
603 glBegin(GL_TRIANGLES)
604 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
605 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
606 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
610 def DrawGCodeLayer(layer, drawQuick = True):
611 filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
612 filamentArea = math.pi * filamentRadius * filamentRadius
613 lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
616 fillColorCycle = [[0.5, 0.5, 0.0, 1], [0.0, 0.5, 0.5, 1], [0.5, 0.0, 0.5, 1]]
617 moveColor = [0, 0, 1, 0.5]
618 retractColor = [1, 0, 0.5, 0.5]
619 supportColor = [0, 1, 1, 1]
620 extrudeColor = [[1, 0, 0, 1], [0, 1, 1, 1], [1, 1, 0, 1], [1, 0, 1, 1]]
621 innerWallColor = [0, 1, 0, 1]
622 skirtColor = [0, 0.5, 0.5, 1]
623 prevPathWasRetract = False
625 glDisable(GL_CULL_FACE)
627 if path.type == 'move':
628 if prevPathWasRetract:
635 if path.type == 'extrude':
636 if path.pathType == 'FILL':
637 c = fillColorCycle[fillCycle]
638 fillCycle = (fillCycle + 1) % len(fillColorCycle)
639 elif path.pathType == 'WALL-INNER':
642 elif path.pathType == 'SUPPORT':
644 elif path.pathType == 'SKIRT':
647 c = extrudeColor[path.extruder]
648 if path.type == 'retract':
650 if path.type == 'extrude' and not drawQuick:
653 for i in xrange(0, len(path.points) - 1):
655 v1 = path.points[i + 1]
657 # Calculate line width from ePerDistance (needs layer thickness and filament diameter)
658 dist = (v0 - v1).vsize()
659 if dist > 0 and path.layerThickness > 0:
660 extrusionMMperDist = (v1.e - v0.e) / dist
661 lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply
663 drawLength += (v0 - v1).vsize()
664 normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1))
667 vv2 = v0 + normal * lineWidth
668 vv3 = v1 + normal * lineWidth
669 vv0 = v0 - normal * lineWidth
670 vv1 = v1 - normal * lineWidth
674 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
675 glVertex3f(vv1.x, vv1.y, vv1.z - zOffset)
676 glVertex3f(vv3.x, vv3.y, vv3.z - zOffset)
677 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
679 if prevNormal is not None:
680 n = (normal + prevNormal)
682 vv4 = v0 + n * lineWidth
683 vv5 = v0 - n * lineWidth
686 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
687 glVertex3f(vv4.x, vv4.y, vv4.z - zOffset)
688 glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset)
689 glVertex3f(v0.x, v0.y, v0.z - zOffset)
691 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
692 glVertex3f(vv5.x, vv5.y, vv5.z - zOffset)
693 glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset)
694 glVertex3f(v0.x, v0.y, v0.z - zOffset)
702 glBegin(GL_TRIANGLES)
703 for v in path.points:
704 glVertex3f(v[0], v[1], v[2])
707 if not path.type == 'move':
708 prevPathWasRetract = False
709 #if path.type == 'retract' and path.points[0].almostEqual(path.points[-1]):
710 # prevPathWasRetract = True
711 glEnable(GL_CULL_FACE)