chiark / gitweb /
Update on the 3D window, now with nice icons. Also added some more error logging...
[cura.git] / Cura / gui / util / opengl.py
1 # coding=utf-8
2 from __future__ import absolute_import
3
4 import math
5 import numpy
6 import wx
7 import time
8
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
13
14 import OpenGL
15
16 OpenGL.ERROR_CHECKING = False
17 from OpenGL.GLUT import *
18 from OpenGL.GLU import *
19 from OpenGL.GL import *
20 glutInit()
21
22 def InitGL(window, view3D, zoom):
23         # set viewing projection
24         glMatrixMode(GL_MODELVIEW)
25         glLoadIdentity()
26         size = window.GetSize()
27         glViewport(0, 0, size.GetWidth(), size.GetHeight())
28
29         glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
30         glLightfv(GL_LIGHT1, GL_POSITION, [1.0, 1.0, 1.0, 0.0])
31
32         glEnable(GL_RESCALE_NORMAL)
33         glEnable(GL_LIGHTING)
34         glEnable(GL_LIGHT0)
35         glEnable(GL_DEPTH_TEST)
36         glEnable(GL_CULL_FACE)
37         glDisable(GL_BLEND)
38
39         glClearColor(1.0, 1.0, 1.0, 1.0)
40         glClearStencil(0)
41         glClearDepth(1.0)
42
43         glMatrixMode(GL_PROJECTION)
44         glLoadIdentity()
45         aspect = float(size.GetWidth()) / float(size.GetHeight())
46         if view3D:
47                 gluPerspective(45.0, aspect, 1.0, 1000.0)
48         else:
49                 glOrtho(-aspect * (zoom), aspect * (zoom), -1.0 * (zoom), 1.0 * (zoom), -1000.0, 1000.0)
50
51         glMatrixMode(GL_MODELVIEW)
52         glLoadIdentity()
53         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
54
55 platformMesh = None
56
57 def DrawMachine(machineSize):
58         glDisable(GL_LIGHTING)
59         glDisable(GL_CULL_FACE)
60         glEnable(GL_BLEND)
61         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
62
63         sx = machineSize.x
64         sy = machineSize.y
65         for x in xrange(-int(sx/20)-1, int(sx / 20) + 1):
66                 for y in xrange(-int(sx/20)-1, int(sy / 20) + 1):
67                         x1 = sx/2+x * 10
68                         x2 = x1 + 10
69                         y1 = sx/2+y * 10
70                         y2 = y1 + 10
71                         x1 = max(min(x1, sx), 0)
72                         y1 = max(min(y1, sy), 0)
73                         x2 = max(min(x2, sx), 0)
74                         y2 = max(min(y2, sy), 0)
75                         if (x & 1) == (y & 1):
76                                 glColor4ub(5, 171, 231, 127)
77                         else:
78                                 glColor4ub(5 * 8 / 10, 171 * 8 / 10, 231 * 8 / 10, 128)
79                         glBegin(GL_QUADS)
80                         glVertex3f(x1, y1, -0.02)
81                         glVertex3f(x2, y1, -0.02)
82                         glVertex3f(x2, y2, -0.02)
83                         glVertex3f(x1, y2, -0.02)
84                         glEnd()
85
86         glEnable(GL_CULL_FACE)
87
88         if profile.getPreference('machine_type') == 'ultimaker':
89                 glPushMatrix()
90                 glEnable(GL_LIGHTING)
91                 glTranslate(100, 200, -1)
92                 glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8])
93                 glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5])
94                 glEnable(GL_BLEND)
95                 glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR)
96
97                 global platformMesh
98                 if platformMesh is None:
99                         try:
100                                 platformMesh = meshLoader.loadMesh(getPathForMesh('ultimaker_platform.stl'))
101                         except:
102                                 platformMesh = False
103
104                 if platformMesh:
105                         DrawMesh(platformMesh)
106                 glPopMatrix()
107                 glDisable(GL_LIGHTING)
108                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
109
110         glColor4ub(5, 171, 231, 64)
111         glBegin(GL_QUADS)
112         glVertex3f(0, 0, machineSize.z)
113         glVertex3f(0, machineSize.y, machineSize.z)
114         glVertex3f(machineSize.x, machineSize.y, machineSize.z)
115         glVertex3f(machineSize.x, 0, machineSize.z)
116         glEnd()
117
118         glColor4ub(5, 171, 231, 96)
119         glBegin(GL_QUADS)
120         glVertex3f(0, 0, 0)
121         glVertex3f(0, 0, machineSize.z)
122         glVertex3f(machineSize.x, 0, machineSize.z)
123         glVertex3f(machineSize.x, 0, 0)
124
125         glVertex3f(0, machineSize.y, machineSize.z)
126         glVertex3f(0, machineSize.y, 0)
127         glVertex3f(machineSize.x, machineSize.y, 0)
128         glVertex3f(machineSize.x, machineSize.y, machineSize.z)
129         glEnd()
130
131         glColor4ub(5, 171, 231, 128)
132         glBegin(GL_QUADS)
133         glVertex3f(0, 0, machineSize.z)
134         glVertex3f(0, 0, 0)
135         glVertex3f(0, machineSize.y, 0)
136         glVertex3f(0, machineSize.y, machineSize.z)
137
138         glVertex3f(machineSize.x, 0, 0)
139         glVertex3f(machineSize.x, 0, machineSize.z)
140         glVertex3f(machineSize.x, machineSize.y, machineSize.z)
141         glVertex3f(machineSize.x, machineSize.y, 0)
142         glEnd()
143
144         glDisable(GL_BLEND)
145
146         #Draw the X/Y/Z indicator
147         glPushMatrix()
148         glTranslate(5, 5, 2)
149         glLineWidth(2)
150         glColor3f(0.5, 0, 0)
151         glBegin(GL_LINES)
152         glVertex3f(0, 0, 0)
153         glVertex3f(20, 0, 0)
154         glEnd()
155         glColor3f(0, 0.5, 0)
156         glBegin(GL_LINES)
157         glVertex3f(0, 0, 0)
158         glVertex3f(0, 20, 0)
159         glEnd()
160         glColor3f(0, 0, 0.5)
161         glBegin(GL_LINES)
162         glVertex3f(0, 0, 0)
163         glVertex3f(0, 0, 20)
164         glEnd()
165
166         glDisable(GL_DEPTH_TEST)
167         #X
168         glColor3f(1, 0, 0)
169         glPushMatrix()
170         glTranslate(20, 0, 0)
171         noZ = ResetMatrixRotationAndScale()
172         glDrawStringCenter("X")
173         glPopMatrix()
174
175         #Y
176         glColor3f(0, 1, 0)
177         glPushMatrix()
178         glTranslate(0, 20, 0)
179         glDrawStringCenter("Y")
180         glPopMatrix()
181
182         #Z
183         if not noZ:
184                 glColor3f(0, 0, 1)
185                 glPushMatrix()
186                 glTranslate(0, 0, 20)
187                 glDrawStringCenter("Z")
188                 glPopMatrix()
189
190         glPopMatrix()
191         glEnable(GL_DEPTH_TEST)
192
193 def glDrawStringCenter(s):
194         glRasterPos2f(0, 0)
195         glBitmap(0,0,0,0, -glGetStringSize(s)[0]/2, 0, None)
196         for c in s:
197                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
198
199 def glGetStringSize(s):
200         width = 0
201         for c in s:
202                 width += glutBitmapWidth(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
203         height = 18
204         return width, height
205
206 def glDrawStringLeft(s):
207         glRasterPos2f(0, 0)
208         for c in s:
209                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
210
211 def glDrawStringRight(s):
212         glRasterPos2f(0, 0)
213         glBitmap(0,0,0,0, -glGetStringSize(s)[0], 0, None)
214         for c in s:
215                 glutBitmapCharacter(OpenGL.GLUT.GLUT_BITMAP_HELVETICA_18, ord(c))
216
217 def glDrawTexturedQuad(x, y, w, h, texID, mirror = 0):
218         tx = float(texID % 4) / 4
219         ty = float(int(texID / 4)) / 8
220         tsx = 0.25
221         tsy = 0.125
222         if mirror & 1:
223                 tx += tsx
224                 tsx = -tsx
225         if mirror & 2:
226                 ty += tsy
227                 tsy = -tsy
228         glPushMatrix()
229         glTranslatef(x, y, 0)
230         glEnable(GL_TEXTURE_2D)
231         glBegin(GL_QUADS)
232         glTexCoord2f(tx+tsx, ty)
233         glVertex2f(w, 0)
234         glTexCoord2f(tx, ty)
235         glVertex2f(0, 0)
236         glTexCoord2f(tx, ty+tsy)
237         glVertex2f(0, h)
238         glTexCoord2f(tx+tsx, ty+tsy)
239         glVertex2f(w, h)
240         glEnd()
241         glPopMatrix()
242
243 def unproject(winx, winy, winz, modelMatrix, projMatrix, viewport):
244         npModelMatrix = numpy.matrix(numpy.array(modelMatrix, numpy.float64).reshape((4,4)))
245         npProjMatrix = numpy.matrix(numpy.array(projMatrix, numpy.float64).reshape((4,4)))
246         finalMatrix = npModelMatrix * npProjMatrix
247         finalMatrix = numpy.linalg.inv(finalMatrix)
248
249         viewport = map(float, viewport)
250         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))
251         vector = (numpy.matrix(vector) * finalMatrix).getA().flatten()
252         ret = list(vector)[0:3] / vector[3]
253         return ret
254
255 def convert3x3MatrixTo4x4(matrix):
256         return list(matrix.getA()[0]) + [0] + list(matrix.getA()[1]) + [0] + list(matrix.getA()[2]) + [0, 0,0,0,1]
257
258 def loadGLTexture(filename):
259         tex = glGenTextures(1)
260         glBindTexture(GL_TEXTURE_2D, tex)
261         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
262         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
263         img = wx.ImageFromBitmap(wx.Bitmap(getPathForImage(filename)))
264         rgbData = img.GetData()
265         alphaData = img.GetAlphaData()
266         if alphaData is not None:
267                 data = ''
268                 for i in xrange(0, len(alphaData)):
269                         data += rgbData[i*3:i*3+3] + alphaData[i]
270                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
271         else:
272                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, rgbData)
273         return tex
274
275 def ResetMatrixRotationAndScale():
276         matrix = glGetFloatv(GL_MODELVIEW_MATRIX)
277         noZ = False
278         if matrix[3][2] > 0:
279                 return False
280         scale2D = matrix[0][0]
281         matrix[0][0] = 1.0
282         matrix[1][0] = 0.0
283         matrix[2][0] = 0.0
284         matrix[0][1] = 0.0
285         matrix[1][1] = 1.0
286         matrix[2][1] = 0.0
287         matrix[0][2] = 0.0
288         matrix[1][2] = 0.0
289         matrix[2][2] = 1.0
290
291         if matrix[3][2] != 0.0:
292                 matrix[3][0] = matrix[3][0] / (-matrix[3][2] / 100)
293                 matrix[3][1] = matrix[3][1] / (-matrix[3][2] / 100)
294                 matrix[3][2] = -100
295         else:
296                 matrix[0][0] = scale2D
297                 matrix[1][1] = scale2D
298                 matrix[2][2] = scale2D
299                 matrix[3][2] = -100
300                 noZ = True
301
302         glLoadMatrixf(matrix)
303         return noZ
304
305
306 def DrawBox(vMin, vMax):
307         glBegin(GL_LINE_LOOP)
308         glVertex3f(vMin[0], vMin[1], vMin[2])
309         glVertex3f(vMax[0], vMin[1], vMin[2])
310         glVertex3f(vMax[0], vMax[1], vMin[2])
311         glVertex3f(vMin[0], vMax[1], vMin[2])
312         glEnd()
313
314         glBegin(GL_LINE_LOOP)
315         glVertex3f(vMin[0], vMin[1], vMax[2])
316         glVertex3f(vMax[0], vMin[1], vMax[2])
317         glVertex3f(vMax[0], vMax[1], vMax[2])
318         glVertex3f(vMin[0], vMax[1], vMax[2])
319         glEnd()
320         glBegin(GL_LINES)
321         glVertex3f(vMin[0], vMin[1], vMin[2])
322         glVertex3f(vMin[0], vMin[1], vMax[2])
323         glVertex3f(vMax[0], vMin[1], vMin[2])
324         glVertex3f(vMax[0], vMin[1], vMax[2])
325         glVertex3f(vMax[0], vMax[1], vMin[2])
326         glVertex3f(vMax[0], vMax[1], vMax[2])
327         glVertex3f(vMin[0], vMax[1], vMin[2])
328         glVertex3f(vMin[0], vMax[1], vMax[2])
329         glEnd()
330
331
332 def DrawMeshOutline(mesh):
333         glEnable(GL_CULL_FACE)
334         glEnableClientState(GL_VERTEX_ARRAY);
335         glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
336
337         glCullFace(GL_FRONT)
338         glLineWidth(3)
339         glPolygonMode(GL_BACK, GL_LINE)
340         glDrawArrays(GL_TRIANGLES, 0, mesh.vertexCount)
341         glPolygonMode(GL_BACK, GL_FILL)
342         glCullFace(GL_BACK)
343
344         glDisableClientState(GL_VERTEX_ARRAY)
345
346
347 def DrawMesh(mesh, insideOut = False):
348         glEnable(GL_CULL_FACE)
349         glEnableClientState(GL_VERTEX_ARRAY);
350         glEnableClientState(GL_NORMAL_ARRAY);
351         glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
352         if insideOut:
353                 glNormalPointer(GL_FLOAT, 0, mesh.invNormal)
354         else:
355                 glNormalPointer(GL_FLOAT, 0, mesh.normal)
356
357         #Odd, drawing in batchs is a LOT faster then drawing it all at once.
358         batchSize = 999    #Warning, batchSize needs to be dividable by 3
359         extraStartPos = int(mesh.vertexCount / batchSize) * batchSize
360         extraCount = mesh.vertexCount - extraStartPos
361
362         glCullFace(GL_BACK)
363         for i in xrange(0, int(mesh.vertexCount / batchSize)):
364                 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
365         glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
366
367         glCullFace(GL_FRONT)
368         if insideOut:
369                 glNormalPointer(GL_FLOAT, 0, mesh.normal)
370         else:
371                 glNormalPointer(GL_FLOAT, 0, mesh.invNormal)
372         for i in xrange(0, int(mesh.vertexCount / batchSize)):
373                 glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
374         extraStartPos = int(mesh.vertexCount / batchSize) * batchSize
375         extraCount = mesh.vertexCount - extraStartPos
376         glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
377         glCullFace(GL_BACK)
378
379         glDisableClientState(GL_VERTEX_ARRAY)
380         glDisableClientState(GL_NORMAL_ARRAY)
381
382
383 def DrawMeshSteep(mesh, matrix, angle):
384         cosAngle = math.sin(angle / 180.0 * math.pi)
385         glDisable(GL_LIGHTING)
386         glDepthFunc(GL_EQUAL)
387         normals = (numpy.matrix(mesh.normal, copy = False) * matrix).getA()
388         for i in xrange(0, int(mesh.vertexCount), 3):
389                 if normals[i][2] < -0.999999:
390                         if mesh.vertexes[i + 0][2] > 0.01:
391                                 glColor3f(0.5, 0, 0)
392                                 glBegin(GL_TRIANGLES)
393                                 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
394                                 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
395                                 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
396                                 glEnd()
397                 elif normals[i][2] < -cosAngle:
398                         glColor3f(-normals[i][2], 0, 0)
399                         glBegin(GL_TRIANGLES)
400                         glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
401                         glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
402                         glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
403                         glEnd()
404                 elif normals[i][2] > 0.999999:
405                         if mesh.vertexes[i + 0][2] > 0.01:
406                                 glColor3f(0.5, 0, 0)
407                                 glBegin(GL_TRIANGLES)
408                                 glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
409                                 glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
410                                 glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
411                                 glEnd()
412                 elif normals[i][2] > cosAngle:
413                         glColor3f(normals[i][2], 0, 0)
414                         glBegin(GL_TRIANGLES)
415                         glVertex3f(mesh.vertexes[i + 0][0], mesh.vertexes[i + 0][1], mesh.vertexes[i + 0][2])
416                         glVertex3f(mesh.vertexes[i + 2][0], mesh.vertexes[i + 2][1], mesh.vertexes[i + 2][2])
417                         glVertex3f(mesh.vertexes[i + 1][0], mesh.vertexes[i + 1][1], mesh.vertexes[i + 1][2])
418                         glEnd()
419         glDepthFunc(GL_LESS)
420
421
422 def DrawGCodeLayer(layer, drawQuick = True):
423         filamentRadius = profile.getProfileSettingFloat('filament_diameter') / 2
424         filamentArea = math.pi * filamentRadius * filamentRadius
425         lineWidth = profile.getProfileSettingFloat('nozzle_size') / 2 / 10
426
427         fillCycle = 0
428         fillColorCycle = [[0.5, 0.5, 0.0, 1], [0.0, 0.5, 0.5, 1], [0.5, 0.0, 0.5, 1]]
429         moveColor = [0, 0, 1, 0.5]
430         retractColor = [1, 0, 0.5, 0.5]
431         supportColor = [0, 1, 1, 1]
432         extrudeColor = [1, 0, 0, 1]
433         innerWallColor = [0, 1, 0, 1]
434         skirtColor = [0, 0.5, 0.5, 1]
435         prevPathWasRetract = False
436
437         glDisable(GL_CULL_FACE)
438         for path in layer:
439                 if path.type == 'move':
440                         if prevPathWasRetract:
441                                 c = retractColor
442                         else:
443                                 c = moveColor
444                         if drawQuick:
445                                 continue
446                 zOffset = 0.01
447                 if path.type == 'extrude':
448                         if path.pathType == 'FILL':
449                                 c = fillColorCycle[fillCycle]
450                                 fillCycle = (fillCycle + 1) % len(fillColorCycle)
451                                 if drawQuick:
452                                         continue
453                         elif path.pathType == 'WALL-INNER':
454                                 c = innerWallColor
455                                 zOffset = 0.02
456                         elif path.pathType == 'SUPPORT':
457                                 c = supportColor
458                         elif path.pathType == 'SKIRT':
459                                 c = skirtColor
460                         else:
461                                 c = extrudeColor
462                 if path.type == 'retract':
463                         c = retractColor
464                 if path.type == 'extrude' and not drawQuick:
465                         drawLength = 0.0
466                         prevNormal = None
467                         for i in xrange(0, len(path.list) - 1):
468                                 v0 = path.list[i]
469                                 v1 = path.list[i + 1]
470
471                                 # Calculate line width from ePerDistance (needs layer thickness and filament diameter)
472                                 dist = (v0 - v1).vsize()
473                                 if dist > 0 and path.layerThickness > 0:
474                                         extrusionMMperDist = (v1.e - v0.e) / dist
475                                         lineWidth = extrusionMMperDist * filamentArea / path.layerThickness / 2 * v1.extrudeAmountMultiply
476
477                                 drawLength += (v0 - v1).vsize()
478                                 normal = (v0 - v1).cross(util3d.Vector3(0, 0, 1))
479                                 normal.normalize()
480
481                                 vv2 = v0 + normal * lineWidth
482                                 vv3 = v1 + normal * lineWidth
483                                 vv0 = v0 - normal * lineWidth
484                                 vv1 = v1 - normal * lineWidth
485
486                                 glBegin(GL_QUADS)
487                                 glColor4fv(c)
488                                 glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
489                                 glVertex3f(vv1.x, vv1.y, vv1.z - zOffset)
490                                 glVertex3f(vv3.x, vv3.y, vv3.z - zOffset)
491                                 glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
492                                 glEnd()
493                                 if prevNormal is not None:
494                                         n = (normal + prevNormal)
495                                         n.normalize()
496                                         vv4 = v0 + n * lineWidth
497                                         vv5 = v0 - n * lineWidth
498                                         glBegin(GL_QUADS)
499                                         glColor4fv(c)
500                                         glVertex3f(vv2.x, vv2.y, vv2.z - zOffset)
501                                         glVertex3f(vv4.x, vv4.y, vv4.z - zOffset)
502                                         glVertex3f(prevVv3.x, prevVv3.y, prevVv3.z - zOffset)
503                                         glVertex3f(v0.x, v0.y, v0.z - zOffset)
504
505                                         glVertex3f(vv0.x, vv0.y, vv0.z - zOffset)
506                                         glVertex3f(vv5.x, vv5.y, vv5.z - zOffset)
507                                         glVertex3f(prevVv1.x, prevVv1.y, prevVv1.z - zOffset)
508                                         glVertex3f(v0.x, v0.y, v0.z - zOffset)
509                                         glEnd()
510
511                                 prevNormal = normal
512                                 prevVv1 = vv1
513                                 prevVv3 = vv3
514                 else:
515                         glBegin(GL_LINE_STRIP)
516                         glColor4fv(c)
517                         for v in path.list:
518                                 glVertex3f(v.x, v.y, v.z)
519                         glEnd()
520                 if not path.type == 'move':
521                         prevPathWasRetract = False
522                 if path.type == 'retract' and path.list[0].almostEqual(path.list[-1]):
523                         prevPathWasRetract = True
524         glEnable(GL_CULL_FACE)