1 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
8 from Cura.util.resources import getPathForImage
12 OpenGL.ERROR_CHECKING = False
13 from OpenGL.GLUT import *
14 from OpenGL.GLU import *
15 from OpenGL.GL import *
16 from OpenGL.GL import shaders
17 glutInit() #Hack; required before glut can be called. Not required for all OS.
19 class GLReferenceCounter(object):
28 return self._refCounter <= 0
30 def hasShaderSupport():
31 if bool(glCreateShader):
35 class GLShader(GLReferenceCounter):
36 def __init__(self, vertexProgram, fragmentProgram):
37 super(GLShader, self).__init__()
38 self._vertexString = vertexProgram
39 self._fragmentString = fragmentProgram
41 vertexShader = shaders.compileShader(vertexProgram, GL_VERTEX_SHADER)
42 fragmentShader = shaders.compileShader(fragmentProgram, GL_FRAGMENT_SHADER)
44 #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.
45 # This is to ensure that this works on intel GPU's
46 # self._program = shaders.compileProgram(self._vertexProgram, self._fragmentProgram)
47 self._program = glCreateProgram()
48 glAttachShader(self._program, vertexShader)
49 glAttachShader(self._program, fragmentShader)
50 glLinkProgram(self._program)
51 # Validation has to occur *after* linking
52 glValidateProgram(self._program)
53 if glGetProgramiv(self._program, GL_VALIDATE_STATUS) == GL_FALSE:
54 raise RuntimeError("Validation failure: %s"%(glGetProgramInfoLog(self._program)))
55 if glGetProgramiv(self._program, GL_LINK_STATUS) == GL_FALSE:
56 raise RuntimeError("Link failure: %s" % (glGetProgramInfoLog(self._program)))
57 glDeleteShader(vertexShader)
58 glDeleteShader(fragmentShader)
59 except RuntimeError, e:
64 if self._program is not None:
65 shaders.glUseProgram(self._program)
68 shaders.glUseProgram(0)
71 if self._program is not None:
72 glDeleteProgram(self._program)
75 def setUniform(self, name, value):
76 if self._program is not None:
77 if type(value) is float:
78 glUniform1f(glGetUniformLocation(self._program, name), value)
79 elif type(value) is numpy.matrix:
80 glUniformMatrix3fv(glGetUniformLocation(self._program, name), 1, False, value.getA().astype(numpy.float32))
82 print 'Unknown type for setUniform: %s' % (str(type(value)))
85 return self._program is not None
87 def getVertexShader(self):
88 return self._vertexString
90 def getFragmentShader(self):
91 return self._fragmentString
94 if self._program is not None and bool(glDeleteProgram):
95 print "Shader was not properly released!"
97 class GLFakeShader(GLReferenceCounter):
99 A Class that acts as an OpenGL shader, but in reality is not one. Used if shaders are not supported.
102 super(GLFakeShader, self).__init__()
105 glEnable(GL_LIGHTING)
107 glEnable(GL_COLOR_MATERIAL)
108 glLightfv(GL_LIGHT0, GL_DIFFUSE, [1,1,1,1])
109 glLightfv(GL_LIGHT0, GL_AMBIENT, [0,0,0,0])
110 glLightfv(GL_LIGHT0, GL_SPECULAR, [0,0,0,0])
113 glDisable(GL_LIGHTING)
118 def setUniform(self, name, value):
124 def getVertexShader(self):
127 def getFragmentShader(self):
130 class GLVBO(GLReferenceCounter):
132 Vertex buffer object. Used for faster rendering.
134 def __init__(self, renderType, vertexArray, normalArray = None, indicesArray = None):
135 super(GLVBO, self).__init__()
136 self._renderType = renderType
137 if not bool(glGenBuffers): # Fallback if buffers are not supported.
138 self._vertexArray = vertexArray
139 self._normalArray = normalArray
140 self._indicesArray = indicesArray
141 self._size = len(vertexArray)
143 self._hasNormals = self._normalArray is not None
144 self._hasIndices = self._indicesArray is not None
146 self._size = len(indicesArray)
148 self._buffer = glGenBuffers(1)
149 self._size = len(vertexArray)
150 self._hasNormals = normalArray is not None
151 self._hasIndices = indicesArray is not None
152 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
153 if self._hasNormals: #TODO: Add size check to see if arrays have same size.
154 glBufferData(GL_ARRAY_BUFFER, numpy.concatenate((vertexArray, normalArray), 1), GL_STATIC_DRAW)
156 glBufferData(GL_ARRAY_BUFFER, vertexArray, GL_STATIC_DRAW)
157 glBindBuffer(GL_ARRAY_BUFFER, 0)
159 self._size = len(indicesArray)
160 self._bufferIndices = glGenBuffers(1)
161 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self._bufferIndices)
162 glBufferData(GL_ELEMENT_ARRAY_BUFFER, numpy.array(indicesArray, numpy.uint32), GL_STATIC_DRAW)
165 glEnableClientState(GL_VERTEX_ARRAY)
166 if self._buffer is None:
167 glVertexPointer(3, GL_FLOAT, 0, self._vertexArray)
169 glEnableClientState(GL_NORMAL_ARRAY)
170 glNormalPointer(GL_FLOAT, 0, self._normalArray)
172 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
174 glEnableClientState(GL_NORMAL_ARRAY)
175 glVertexPointer(3, GL_FLOAT, 2*3*4, c_void_p(0))
176 glNormalPointer(GL_FLOAT, 2*3*4, c_void_p(3 * 4))
178 glVertexPointer(3, GL_FLOAT, 3*4, c_void_p(0))
180 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self._bufferIndices)
183 if self._buffer is None:
184 glDrawElements(self._renderType, self._size, GL_UNSIGNED_INT, self._indicesArray)
186 glDrawElements(self._renderType, self._size, GL_UNSIGNED_INT, c_void_p(0))
188 batchSize = 996 #Warning, batchSize needs to be dividable by 4 (quads), 3 (triangles) and 2 (lines). Current value is magic.
189 extraStartPos = int(self._size / batchSize) * batchSize #leftovers.
190 extraCount = self._size - extraStartPos
191 for i in xrange(0, int(self._size / batchSize)):
192 glDrawArrays(self._renderType, i * batchSize, batchSize)
193 glDrawArrays(self._renderType, extraStartPos, extraCount)
195 if self._buffer is not None:
196 glBindBuffer(GL_ARRAY_BUFFER, 0)
198 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
200 glDisableClientState(GL_VERTEX_ARRAY)
202 glDisableClientState(GL_NORMAL_ARRAY)
205 if self._buffer is not None:
206 glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
207 glBufferData(GL_ARRAY_BUFFER, None, GL_STATIC_DRAW)
208 glBindBuffer(GL_ARRAY_BUFFER, 0)
209 glDeleteBuffers(1, [self._buffer])
211 self._vertexArray = None
212 self._normalArray = None
215 if self._buffer is not None and bool(glDeleteBuffers):
216 print "VBO was not properly released!"
218 def glDrawStringCenter(s):
220 Draw string on current draw pointer position
223 glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
225 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
227 def glGetStringSize(s):
229 Get size in pixels of string
233 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
237 def glDrawStringLeft(s):
243 glTranslate(0, 18 * n, 0)
248 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
250 def glDrawStringRight(s):
252 glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
254 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
256 def glDrawQuad(x, y, w, h):
258 glTranslatef(x, y, 0)
259 glDisable(GL_TEXTURE_2D)
268 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
269 tx = float(texID % 4) / 4
270 ty = float(int(texID / 4)) / 8
280 glTranslatef(x, y, 0)
281 glEnable(GL_TEXTURE_2D)
283 glTexCoord2f(tx+tsx, ty)
287 glTexCoord2f(tx, ty+tsy)
289 glTexCoord2f(tx+tsx, ty+tsy)
294 def glDrawStretchedQuad(x, y, w, h, cornerSize, texID):
296 Same as draw texured quad, but without stretching the corners. Useful for resizable windows.
298 tx0 = float(texID % 4) / 4
299 ty0 = float(int(texID / 4)) / 8
300 tx1 = tx0 + 0.25 / 2.0
301 ty1 = ty0 + 0.125 / 2.0
306 glTranslatef(x, y, 0)
307 glEnable(GL_TEXTURE_2D)
310 glTexCoord2f(tx1, ty0)
311 glVertex2f( cornerSize, 0)
312 glTexCoord2f(tx0, ty0)
314 glTexCoord2f(tx0, ty1)
315 glVertex2f( 0, cornerSize)
316 glTexCoord2f(tx1, ty1)
317 glVertex2f( cornerSize, cornerSize)
319 glTexCoord2f(tx2, ty0)
321 glTexCoord2f(tx1, ty0)
322 glVertex2f( w - cornerSize, 0)
323 glTexCoord2f(tx1, ty1)
324 glVertex2f( w - cornerSize, cornerSize)
325 glTexCoord2f(tx2, ty1)
326 glVertex2f( w, cornerSize)
328 glTexCoord2f(tx1, ty1)
329 glVertex2f( cornerSize, h - cornerSize)
330 glTexCoord2f(tx0, ty1)
331 glVertex2f( 0, h - cornerSize)
332 glTexCoord2f(tx0, ty2)
334 glTexCoord2f(tx1, ty2)
335 glVertex2f( cornerSize, h)
337 glTexCoord2f(tx2, ty1)
338 glVertex2f( w, h - cornerSize)
339 glTexCoord2f(tx1, ty1)
340 glVertex2f( w - cornerSize, h - cornerSize)
341 glTexCoord2f(tx1, ty2)
342 glVertex2f( w - cornerSize, h)
343 glTexCoord2f(tx2, ty2)
347 glTexCoord2f(tx1, ty1)
348 glVertex2f( w-cornerSize, cornerSize)
349 glTexCoord2f(tx1, ty1)
350 glVertex2f( cornerSize, cornerSize)
351 glTexCoord2f(tx1, ty1)
352 glVertex2f( cornerSize, h-cornerSize)
353 glTexCoord2f(tx1, ty1)
354 glVertex2f( w-cornerSize, h-cornerSize)
357 glTexCoord2f(tx2, ty1)
358 glVertex2f( w, cornerSize)
359 glTexCoord2f(tx1, ty1)
360 glVertex2f( w-cornerSize, cornerSize)
361 glTexCoord2f(tx1, ty1)
362 glVertex2f( w-cornerSize, h-cornerSize)
363 glTexCoord2f(tx2, ty1)
364 glVertex2f( w, h-cornerSize)
367 glTexCoord2f(tx1, ty1)
368 glVertex2f( cornerSize, cornerSize)
369 glTexCoord2f(tx0, ty1)
370 glVertex2f( 0, cornerSize)
371 glTexCoord2f(tx0, ty1)
372 glVertex2f( 0, h-cornerSize)
373 glTexCoord2f(tx1, ty1)
374 glVertex2f( cornerSize, h-cornerSize)
377 glTexCoord2f(tx1, ty0)
378 glVertex2f( w-cornerSize, 0)
379 glTexCoord2f(tx1, ty0)
380 glVertex2f( cornerSize, 0)
381 glTexCoord2f(tx1, ty1)
382 glVertex2f( cornerSize, cornerSize)
383 glTexCoord2f(tx1, ty1)
384 glVertex2f( w-cornerSize, cornerSize)
387 glTexCoord2f(tx1, ty1)
388 glVertex2f( w-cornerSize, h-cornerSize)
389 glTexCoord2f(tx1, ty1)
390 glVertex2f( cornerSize, h-cornerSize)
391 glTexCoord2f(tx1, ty2)
392 glVertex2f( cornerSize, h)
393 glTexCoord2f(tx1, ty2)
394 glVertex2f( w-cornerSize, h)
397 glDisable(GL_TEXTURE_2D)
400 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
402 Projects window position to 3D space. (gluUnProject). Reimplentation as some drivers crash with the original.
404 npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
405 npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
406 finalMatrix = npModelMatrix * npProjMatrix
407 finalMatrix = numpy.linalg.inv(finalMatrix)
409 viewport = map(float, viewport)
410 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))
411 vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
412 ret = list(vector)[0:3] / vector[3]
415 def convert3x3MatrixTo4x4(matrix):
416 return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
418 def loadGLTexture(filename):
419 tex = glGenTextures(1)
420 glBindTexture(GL_TEXTURE_2D, tex)
421 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
422 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
423 img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
424 rgbData = img.GetData()
425 alphaData = img.GetAlphaData()
426 if alphaData is not None:
428 for i in xrange(0, len(alphaData)):
429 data += rgbData[i*3:i*3+3] + alphaData[i]
430 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
432 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
435 def DrawBox(vMin, vMax):
436 """ Draw wireframe box
438 glBegin(GL_LINE_LOOP)
439 glVertex3f(vMin[0], vMin[1], vMin[2])
440 glVertex3f(vMax[0], vMin[1], vMin[2])
441 glVertex3f(vMax[0], vMax[1], vMin[2])
442 glVertex3f(vMin[0], vMax[1], vMin[2])
445 glBegin(GL_LINE_LOOP)
446 glVertex3f(vMin[0], vMin[1], vMax[2])
447 glVertex3f(vMax[0], vMin[1], vMax[2])
448 glVertex3f(vMax[0], vMax[1], vMax[2])
449 glVertex3f(vMin[0], vMax[1], vMax[2])
452 glVertex3f(vMin[0], vMin[1], vMin[2])
453 glVertex3f(vMin[0], vMin[1], vMax[2])
454 glVertex3f(vMax[0], vMin[1], vMin[2])
455 glVertex3f(vMax[0], vMin[1], vMax[2])
456 glVertex3f(vMax[0], vMax[1], vMin[2])
457 glVertex3f(vMax[0], vMax[1], vMax[2])
458 glVertex3f(vMin[0], vMax[1], vMin[2])
459 glVertex3f(vMin[0], vMax[1], vMax[2])