from Cura.gui import configBase
from Cura.gui import expertConfig
-from Cura.gui import preview3d
-from Cura.gui import sliceProgressPanel
from Cura.gui import alterationPanel
from Cura.gui import pluginPanel
from Cura.gui import preferencesDialog
from Cura.gui import printWindow
from Cura.gui import simpleMode
from Cura.gui import projectPlanner
+from Cura.gui import sceneView
from Cura.gui.tools import batchRun
-from Cura.gui import flatSlicerWindow
from Cura.gui.util import dropTarget
from Cura.gui.tools import minecraftImport
-from Cura.util import validators
from Cura.util import profile
from Cura.util import version
from Cura.util import sliceRun
self.leftPane.SetSizer(self.leftSizer)
#Preview window
- self.preview3d = preview3d.previewPanel(self.rightPane)
+ self.scene = sceneView.SceneView(self.rightPane)
#Also bind double clicking the 3D preview to load an STL file.
#self.preview3d.glCanvas.Bind(wx.EVT_LEFT_DCLICK, lambda e: self._showModelLoadDialog(1), self.preview3d.glCanvas)
#Main sizer, to position the preview window, buttons and tab control
sizer = wx.BoxSizer()
self.rightPane.SetSizer(sizer)
- sizer.Add(self.preview3d, 1, flag=wx.EXPAND)
+ sizer.Add(self.scene, 1, flag=wx.EXPAND)
# Main window sizer
sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer = sizer
if len(self.filelist) > 0:
- self.preview3d.loadModelFiles(self.filelist)
+ self.scene.loadScene(self.filelist)
# Update the Model MRU
for idx in xrange(0, len(self.filelist)):
self.filelist = filelist
self.SetTitle('Cura - %s - %s' % (version.getVersion(), filelist[-1]))
profile.putPreference('lastFile', ';'.join(self.filelist))
- self.preview3d.loadModelFiles(self.filelist, True)
- self.preview3d.setViewMode("Normal")
+ self.scene.loadScene(self.filelist)
+ #self.preview3d.setViewMode("Normal")
# Update the Model MRU
for idx in xrange(0, len(self.filelist)):
self.sizer.Layout()
def updateProfileToControls(self):
- self.preview3d.updateProfileToControls()
+ self.scene.updateProfileToControls()
self.normalSettingsPanel.updateProfileToControls()
self.simpleSettingsPanel.updateProfileToControls()
profile.putPreference('window_normal_sash', self.normalSashPos)
#HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which keeps wxWidgets from quiting.
- self.preview3d.glCanvas.OnPaint = lambda e : e
+ self.scene.OnPaint = lambda e : e
self.Destroy()
def OnQuit(self, e):
import time
import os
import numpy
+import traceback
+import sys
from wx import glcanvas
import wx
if obj.filename is not None and os.path.isfile(obj.filename) and obj.fileTime != os.stat(obj.filename).st_mtime:
obj.fileTime = os.stat(obj.filename).st_mtime
try:
- mesh = meshLoader.loadMesh(obj.filename)
+ mesh = meshLoader.loadMeshes(obj.filename)[0]
except:
+ traceback.print_exc(file=sys.stdout)
wx.CallAfter(self.ShowWarningPopup, 'Failed to load %s' % (obj.filename))
obj.mesh = None
obj.filename = None
minV = self.objectList[0].mesh.getMinimum()
maxV = self.objectList[0].mesh.getMaximum()
- objectsBoundaryCircleSize = self.objectList[0].mesh.boundaryCircleSize
+ objectsBoundaryCircleSize = self.objectList[0].mesh.getBoundaryCircle()
for obj in self.objectList:
if obj.mesh is None:
continue
minV = numpy.minimum(minV, obj.mesh.getMinimum())
maxV = numpy.maximum(maxV, obj.mesh.getMaximum())
- objectsBoundaryCircleSize = max(objectsBoundaryCircleSize, obj.mesh.boundaryCircleSize)
+ objectsBoundaryCircleSize = max(objectsBoundaryCircleSize, obj.mesh.getBoundaryCircle())
self.objectsMaxV = maxV
self.objectsMinV = minV
--- /dev/null
+from __future__ import absolute_import
+from __future__ import division
+
+import numpy
+from ctypes import c_void_p
+
+import OpenGL
+OpenGL.ERROR_CHECKING = False
+from OpenGL.GLU import *
+from OpenGL.GL import *
+
+from Cura.util import profile
+from Cura.util import meshLoader
+from Cura.gui.util import opengl
+from Cura.gui.util import openglGui
+
+class SceneView(openglGui.glGuiPanel):
+ def __init__(self, parent):
+ super(SceneView, self).__init__(parent)
+
+ self._yaw = 30
+ self._pitch = 60
+ self._zoom = 100
+ self._objectList = []
+ self._objectShader = None
+ self._objColors = [None,None,None,None]
+ self._tmpVertex = None
+ self.updateProfileToControls()
+
+ def loadScene(self, fileList):
+ for filename in fileList:
+ for obj in meshLoader.loadMeshes(filename):
+ self._objectList.append(obj)
+
+ def updateProfileToControls(self):
+ self._objColors[0] = profile.getPreferenceColour('model_colour')
+ self._objColors[1] = profile.getPreferenceColour('model_colour2')
+ self._objColors[2] = profile.getPreferenceColour('model_colour3')
+ self._objColors[3] = profile.getPreferenceColour('model_colour4')
+
+ def OnMouseMotion(self,e):
+ if e.Dragging() and e.LeftIsDown():
+ self._yaw += e.GetX() - self.oldX
+ self._pitch -= e.GetY() - self.oldY
+ if self._pitch > 170:
+ self._pitch = 170
+ if self._pitch < 10:
+ self._pitch = 10
+ if e.Dragging() and e.RightIsDown():
+ self._zoom += e.GetY() - self.oldY
+ if self._zoom < 1:
+ self._zoom = 1
+ if self._zoom > 500:
+ self._zoom = 500
+ self.oldX = e.GetX()
+ self.oldY = e.GetY()
+
+ def _init3DView(self):
+ # set viewing projection
+ size = self.GetSize()
+ glViewport(0, 0, size.GetWidth(), size.GetHeight())
+
+ glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
+
+ glDisable(GL_RESCALE_NORMAL)
+ glDisable(GL_LIGHTING)
+ glDisable(GL_LIGHT0)
+ glEnable(GL_DEPTH_TEST)
+ glDisable(GL_CULL_FACE)
+ glDisable(GL_BLEND)
+
+ glClearColor(0.8, 0.8, 0.8, 1.0)
+ glClearStencil(0)
+ glClearDepth(1.0)
+
+ glMatrixMode(GL_PROJECTION)
+ glLoadIdentity()
+ aspect = float(size.GetWidth()) / float(size.GetHeight())
+ gluPerspective(45.0, aspect, 1.0, 1000.0)
+
+ glMatrixMode(GL_MODELVIEW)
+ glLoadIdentity()
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
+
+ def OnPaint(self,e):
+ if self._objectShader is None:
+ self._objectShader = opengl.GLShader("""
+uniform float cameraDistance;
+varying float light_amount;
+
+void main(void)
+{
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+ gl_FrontColor = gl_Color;
+
+ light_amount = abs(dot(normalize(gl_NormalMatrix * gl_Normal), normalize(gl_LightSource[0].position.xyz)));
+ light_amount *= 1 - (length(gl_Position.xyz - vec3(0,0,cameraDistance)) / 1.5 / cameraDistance);
+ light_amount += 0.2;
+}
+ ""","""
+uniform float cameraDistance;
+varying float light_amount;
+
+void main(void)
+{
+ gl_FragColor = gl_Color * light_amount;
+}
+ """)
+ self._init3DView()
+ glTranslate(0,0,-self._zoom)
+ glRotate(-self._pitch, 1,0,0)
+ glRotate(self._yaw, 0,0,1)
+ glTranslate(0,0,-15)
+ glColor3f(self._objColors[0][0], self._objColors[0][1], self._objColors[0][2])
+
+ self._objectShader.bind()
+ self._objectShader.setUniform('cameraDistance', self._zoom)
+ if self._tmpVertex is None:
+ for obj in self._objectList:
+ for m in obj._meshList:
+ self._tmpVertex = opengl.GLVBO(m.vertexes, m.normal)
+
+ self._tmpVertex.render()
+ self._objectShader.unbind()
from OpenGL.GLUT import *
from OpenGL.GLU import *
from OpenGL.GL import *
+from OpenGL.GL import shaders
glutInit()
-def InitGL(window, view3D, zoom):
- # set viewing projection
- glMatrixMode(GL_MODELVIEW)
- glLoadIdentity()
- size = window.GetSize()
- glViewport(0, 0, size.GetWidth(), size.GetHeight())
-
- glLightfv(GL_LIGHT0, GL_POSITION, [0.2, 0.2, 1.0, 0.0])
- glLightfv(GL_LIGHT1, GL_POSITION, [1.0, 1.0, 1.0, 0.0])
-
- glEnable(GL_RESCALE_NORMAL)
- glEnable(GL_LIGHTING)
- glEnable(GL_LIGHT0)
- glEnable(GL_DEPTH_TEST)
- glEnable(GL_CULL_FACE)
- glDisable(GL_BLEND)
-
- glClearColor(1.0, 1.0, 1.0, 1.0)
- glClearColor(0.8, 0.8, 0.8, 1.0)
- glClearStencil(0)
- glClearDepth(1.0)
-
- glMatrixMode(GL_PROJECTION)
- glLoadIdentity()
- aspect = float(size.GetWidth()) / float(size.GetHeight())
- if view3D:
- gluPerspective(45.0, aspect, 1.0, 1000.0)
- else:
- glOrtho(-aspect * (zoom), aspect * (zoom), -1.0 * (zoom), 1.0 * (zoom), -1000.0, 1000.0)
-
- glMatrixMode(GL_MODELVIEW)
- glLoadIdentity()
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
-
platformMesh = None
+class GLShader(object):
+ def __init__(self, vertexProgram, fragmentProgram):
+ try:
+ self._vertexProgram = shaders.compileShader(vertexProgram, GL_VERTEX_SHADER)
+ self._fragmentProgram = shaders.compileShader(fragmentProgram, GL_FRAGMENT_SHADER)
+ self._program = shaders.compileProgram(self._vertexProgram, self._fragmentProgram)
+ except RuntimeError, e:
+ print "Shader error:"
+ print str(e)
+ self._program = None
+
+ def bind(self):
+ if self._program is not None:
+ shaders.glUseProgram(self._program)
+
+ def unbind(self):
+ shaders.glUseProgram(0)
+
+ def delete(self):
+ shaders.glDeleteShader(self._vertexProgram)
+ shaders.glDeleteShader(self._fragmentProgram)
+ glDeleteProgram(self._program)
+
+ def setUniform(self, name, value):
+ glUniform1f(glGetUniformLocation(self._program, name), value)
+
+class GLVBO(object):
+ def __init__(self, vertexArray, normalArray):
+ self._buffer = glGenBuffers(1)
+ self._size = len(vertexArray)
+ glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
+ glBufferData(GL_ARRAY_BUFFER, numpy.concatenate((vertexArray, normalArray), 1), GL_STATIC_DRAW)
+ glBindBuffer(GL_ARRAY_BUFFER, 0)
+
+ def render(self):
+ glEnableClientState(GL_VERTEX_ARRAY)
+ glEnableClientState(GL_NORMAL_ARRAY)
+ #glVertexPointer(3, GL_FLOAT, 0, m.vertexes)
+ #glNormalPointer(GL_FLOAT, 0, m.normal)
+ glBindBuffer(GL_ARRAY_BUFFER, self._buffer)
+ glVertexPointer(3, GL_FLOAT, 2*3*4, c_void_p(0))
+ glNormalPointer(GL_FLOAT, 2*3*4, c_void_p(3 * 4))
+
+ batchSize = 999 #Warning, batchSize needs to be dividable by 3
+ extraStartPos = int(self._size / batchSize) * batchSize
+ extraCount = self._size - extraStartPos
+
+ for i in xrange(0, int(self._size / batchSize)):
+ glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
+ glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
+ glBindBuffer(GL_ARRAY_BUFFER, 0)
+ glDisableClientState(GL_VERTEX_ARRAY)
+ glDisableClientState(GL_NORMAL_ARRAY)
+
+ def release(self):
+ if self._buffer is not None:
+ glDeleteBuffers(self._buffer, 1)
+ self._buffer = None
+
+ def __del__(self):
+ if self._buffer is not None:
+ print "OpenGL buffer was not properly cleaned, trying to clean it up now."
+ glDeleteBuffers(self._buffer, 1)
+
def DrawMachine(machineSize):
glDisable(GL_LIGHTING)
glDisable(GL_CULL_FACE)
def DrawMesh(mesh, insideOut = False):
glEnable(GL_CULL_FACE)
- glEnableClientState(GL_VERTEX_ARRAY);
- glEnableClientState(GL_NORMAL_ARRAY);
- glVertexPointer(3, GL_FLOAT, 0, mesh.vertexes)
- if insideOut:
- glNormalPointer(GL_FLOAT, 0, mesh.invNormal)
- else:
- glNormalPointer(GL_FLOAT, 0, mesh.normal)
+ glEnableClientState(GL_VERTEX_ARRAY)
+ glEnableClientState(GL_NORMAL_ARRAY)
+ for m in mesh._meshList:
+ glVertexPointer(3, GL_FLOAT, 0, m.vertexes)
+ if insideOut:
+ glNormalPointer(GL_FLOAT, 0, m.invNormal)
+ else:
+ glNormalPointer(GL_FLOAT, 0, m.normal)
- #Odd, drawing in batchs is a LOT faster then drawing it all at once.
- batchSize = 999 #Warning, batchSize needs to be dividable by 3
- extraStartPos = int(mesh.vertexCount / batchSize) * batchSize
- extraCount = mesh.vertexCount - extraStartPos
+ #Odd, drawing in batchs is a LOT faster then drawing it all at once.
+ batchSize = 999 #Warning, batchSize needs to be dividable by 3
+ extraStartPos = int(m.vertexCount / batchSize) * batchSize
+ extraCount = m.vertexCount - extraStartPos
- glCullFace(GL_BACK)
- for i in xrange(0, int(mesh.vertexCount / batchSize)):
- glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
- glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
+ glCullFace(GL_BACK)
+ for i in xrange(0, int(m.vertexCount / batchSize)):
+ glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
+ glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
- glCullFace(GL_FRONT)
- if insideOut:
- glNormalPointer(GL_FLOAT, 0, mesh.normal)
- else:
- glNormalPointer(GL_FLOAT, 0, mesh.invNormal)
- for i in xrange(0, int(mesh.vertexCount / batchSize)):
- glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
- extraStartPos = int(mesh.vertexCount / batchSize) * batchSize
- extraCount = mesh.vertexCount - extraStartPos
- glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
- glCullFace(GL_BACK)
+ glCullFace(GL_FRONT)
+ if insideOut:
+ glNormalPointer(GL_FLOAT, 0, m.normal)
+ else:
+ glNormalPointer(GL_FLOAT, 0, m.invNormal)
+ for i in xrange(0, int(m.vertexCount / batchSize)):
+ glDrawArrays(GL_TRIANGLES, i * batchSize, batchSize)
+ extraStartPos = int(m.vertexCount / batchSize) * batchSize
+ extraCount = m.vertexCount - extraStartPos
+ glDrawArrays(GL_TRIANGLES, extraStartPos, extraCount)
+ glCullFace(GL_BACK)
glDisableClientState(GL_VERTEX_ARRAY)
glDisableClientState(GL_NORMAL_ARRAY)
glDisable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glDisable(GL_LIGHTING)
glColor4ub(255,255,255,255)
self._meshList = []
self._position = [0.0, 0.0]
self._matrix = numpy.matrix([[1,0,0],[0,1,0],[0,0,1]], numpy.float64)
+ self._transformedMin = None
+ self._transformedMax = None
+ self._boundaryCircleSize = None
def _addMesh(self):
m = mesh()
self._meshList.append(m)
return m
+ def _postProcessAfterLoad(self):
+ for m in self._meshList:
+ m._calculateNormals()
+ self.processMatrix()
+
def processMatrix(self):
- self.transformedMin = numpy.array([999999999999,999999999999,999999999999], numpy.float64)
- self.transformedMax = numpy.array([-999999999999,-999999999999,-999999999999], numpy.float64)
- self.boundaryCircleSize = 0
+ self._transformedMin = numpy.array([999999999999,999999999999,999999999999], numpy.float64)
+ self._transformedMax = numpy.array([-999999999999,-999999999999,-999999999999], numpy.float64)
+ self._boundaryCircleSize = 0
for m in self._meshList:
- transformedVertexes = (numpy.matrix(m.vertexes, copy = False) * self.matrix).getA()
+ transformedVertexes = (numpy.matrix(m.vertexes, copy = False) * self._matrix).getA()
transformedMin = transformedVertexes.min(0)
transformedMax = transformedVertexes.max(0)
for n in xrange(0, 3):
- self.transformedMin[n] = min(transformedMin[n], self.transformedMin[n])
- self.transformedMax[n] = min(transformedMax[n], self.transformedMax[n])
+ self._transformedMin[n] = min(transformedMin[n], self._transformedMin[n])
+ self._transformedMax[n] = max(transformedMax[n], self._transformedMax[n])
#Calculate the boundary circle
transformedSize = transformedMax - transformedMin
center = transformedMin + transformedSize / 2.0
boundaryCircleSize = round(math.sqrt(numpy.max(((transformedVertexes[::,0] - center[0]) * (transformedVertexes[::,0] - center[0])) + ((transformedVertexes[::,1] - center[1]) * (transformedVertexes[::,1] - center[1])) + ((transformedVertexes[::,2] - center[2]) * (transformedVertexes[::,2] - center[2])))), 3)
- self.boundaryCircleSize = max(self.boundaryCircleSize, boundaryCircleSize)
- self.transformedSize = self.transformedMax - self.transformedMin
+ self._boundaryCircleSize = max(self._boundaryCircleSize, boundaryCircleSize)
+ self._transformedSize = self._transformedMax - self._transformedMin
def getMaximum(self):
- return self.transformedMax
+ return self._transformedMax
def getMinimum(self):
- return self.transformedMin
+ return self._transformedMin
def getSize(self):
- return self.transformedSize
+ return self._transformedSize
+ def getBoundaryCircle(self):
+ return self._boundaryCircleSize
class mesh(object):
def __init__(self):
self.vertexes = None
self.vertexCount = 0
- def _addVertex(self, x, y, z):
+ def _addFace(self, x0, y0, z0, x1, y1, z1, x2, y2, z2):
n = self.vertexCount
- self.vertexes[n][0] = x
- self.vertexes[n][1] = y
- self.vertexes[n][2] = z
- self.vertexCount += 1
+ self.vertexes[n][0] = x0
+ self.vertexes[n][1] = y0
+ self.vertexes[n][2] = z0
+ n += 1
+ self.vertexes[n][0] = x1
+ self.vertexes[n][1] = y1
+ self.vertexes[n][2] = z1
+ n += 1
+ self.vertexes[n][0] = x2
+ self.vertexes[n][1] = y2
+ self.vertexes[n][2] = z2
+ self.vertexCount += 3
- def _prepareVertexCount(self, vertexNumber):
+ def _prepareFaceCount(self, faceNumber):
#Set the amount of faces before loading data in them. This way we can create the numpy arrays before we fill them.
- self.vertexes = numpy.zeros((vertexNumber, 3), numpy.float32)
- self.normal = numpy.zeros((vertexNumber, 3), numpy.float32)
+ self.vertexes = numpy.zeros((faceNumber*3, 3), numpy.float32)
+ self.normal = numpy.zeros((faceNumber*3, 3), numpy.float32)
self.vertexCount = 0
- def _postProcessAfterLoad(self):
- self.processMatrix()
- self._calculateNormals()
-
def _calculateNormals(self):
#Calculate the normals
tris = self.vertexes.reshape(self.vertexCount / 3, 3, 3)
wildcardList = ';'.join(map(lambda s: '*' + s, supportedExtensions()))
return "Mesh files (%s)|%s;%s" % (wildcardList, wildcardList, wildcardList.upper())
-def loadMesh(filename):
+#loadMeshes loads 1 or more printableObjects from a file.
+# STL files are a single printableObject with a single mesh, these are most common.
+# OBJ files usually contain a single mesh, but they can contain multiple meshes
+# AMF can contain whole scenes of objects with each object having multiple meshes.
+# DAE files are a mess, but they can contain scenes of objects as well as grouped meshes
+
+def loadMeshes(filename):
ext = filename[filename.rfind('.'):].lower()
if ext == '.stl':
- return stl.stlModel().load(filename)
+ return stl.loadSTLscene(filename)
if ext == '.obj':
return obj.objModel().load(filename)
if ext == '.dae':
if ext == '.amf':
return amf.amfModel().load(filename)
print 'Error: Unknown model extension: %s' % (ext)
- return None
+ return []
from Cura.util import mesh
-class stlModel(mesh.mesh):
- def __init__(self):
- super(stlModel, self).__init__()
+def _loadAscii(m, f):
+ cnt = 0
+ for lines in f:
+ for line in lines.split('\r'):
+ if 'vertex' in line:
+ cnt += 1
+ m._prepareFaceCount(int(cnt) / 3)
+ f.seek(5, os.SEEK_SET)
+ cnt = 0
+ data = [None,None,None]
+ for lines in f:
+ for line in lines.split('\r'):
+ if 'vertex' in line:
+ data[cnt] = line.split()[1:]
+ cnt += 1
+ if cnt == 3:
+ m._addFace(float(data[0][0]), float(data[0][1]), float(data[0][2]), float(data[1][0]), float(data[1][1]), float(data[1][2]), float(data[2][0]), float(data[2][1]), float(data[2][2]))
+ cnt = 0
- def load(self, filename):
- f = open(filename, "rb")
- if f.read(5).lower() == "solid":
- self._loadAscii(f)
- if self.vertexCount < 3:
- f.seek(5, os.SEEK_SET)
- self._loadBinary(f)
- else:
- self._loadBinary(f)
- f.close()
- self._postProcessAfterLoad()
- return self
-
- def _loadAscii(self, f):
- cnt = 0
- for lines in f:
- for line in lines.split('\r'):
- if 'vertex' in line:
- cnt += 1
- self._prepareVertexCount(int(cnt))
- f.seek(5, os.SEEK_SET)
- cnt = 0
- for lines in f:
- for line in lines.split('\r'):
- if 'vertex' in line:
- data = line.split()
- self.addVertex(float(data[1]), float(data[2]), float(data[3]))
+def _loadBinary(m, f):
+ #Skip the header
+ f.read(80-5)
+ faceCount = struct.unpack('<I', f.read(4))[0]
+ m._prepareFaceCount(faceCount)
+ for idx in xrange(0, faceCount):
+ data = struct.unpack("<ffffffffffffH", f.read(50))
+ m._addFace(data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11])
- def _loadBinary(self, f):
- #Skip the header
- f.read(80-5)
- faceCount = struct.unpack('<I', f.read(4))[0]
- self._prepareVertexCount(faceCount * 3)
- for idx in xrange(0, faceCount):
- data = struct.unpack("<ffffffffffffH", f.read(50))
- self.addVertex(data[3], data[4], data[5])
- self.addVertex(data[6], data[7], data[8])
- self.addVertex(data[9], data[10], data[11])
+def loadSTLscene(filename):
+ obj = mesh.printableObject()
+ m = obj._addMesh()
+
+ f = open(filename, "rb")
+ if f.read(5).lower() == "solid":
+ _loadAscii(m, f)
+ if m.vertexCount < 3:
+ f.seek(5, os.SEEK_SET)
+ _loadBinary(m, f)
+ else:
+ _loadBinary(m, f)
+ f.close()
+ obj._postProcessAfterLoad()
+ return [obj]
def saveAsSTL(mesh, filename):
f = open(filename, 'wb')