[Bf-python] obj importer 4x speedup
Campbell Barton
cbarton at metavr.com
Sun Sep 12 03:32:13 CEST 2004
Hi Jiri, Id like some more OBJ files, thanks-
Also, I should realy have used string formatting- Its about 30% faster
I modified the exporter accordingly.
- From this test
from Blender import *
time1 = sys.time()
file = open('/d', 'w')
for x in range(10000):
file.write( str(1.123) + str(100.9) + str(33.333) + str(10.9))
file.close()
print "time A: ", sys.time() - time1
time1 = sys.time()
file = open('/d', 'w')
for x in range(10000):
file.write('%s%s%s%s' % (1.123, 100.9, 33.333, 10.9))
file.close()
print "time B: ", sys.time() - time1
Jiri Hnidek wrote:
> Hi Cam,
> good stuff. I replaced eval() with float() in the rest of code, and I
> fixed bug #1560. Let's have look:
>
> https://projects.blender.org/tracker/?func=detail&aid=1560&group_id=9&atid=125
>
>
> Tri's and fans are imported correctly now.
>
> Example of obj file:
>
> o octagon
> v 10.000000 0.000000 0.000000
> v 7.071068 0.000000 7.071068
> v 0.000000 0.000000 10.000000
> v -7.071068 0.000000 7.071068
> v -10.000000 0.000000 0.000000
> v -7.071068 0.000000 -7.071068
> v -0.000000 0.000000 -10.000000
> v 7.071068 0.000000 -7.071068
> f 1 2 3 4 5 6 7 8
>
> ianwill: Please, don't commit obj_import.py and obj_export.py to the
> cvs repository. You rewrited my changes for several times :-(. Send
> these files to me and I will commit it. Cam should have cvs access to
> the repository! ;-)
>
> Cam: I have some other obj files for testing, are you interested?
>
> Jiri
>
> Campbell Barton wrote:
>
>> Hi. I have used eval(somestring) in my obj importer- This slow and
>> not a good idea
>> I replaced eval() with float() and int() both wich can handle strings
>> (I didnt know that)
>> - Cam
>
>
>
>------------------------------------------------------------------------
>
>#!BPY
>
>"""
>Name: 'Wavefront (.obj)...'
>Blender: 232
>Group: 'Import'
>Tooltip: 'Load a Wavefront OBJ File'
>"""
>
># $Id: obj_import.py,v 1.9 2004/08/04 06:16:45 ianwill Exp $
>#
># --------------------------------------------------------------------------
># OBJ Import v0.9 by Campbell Barton (AKA Ideasman)
># --------------------------------------------------------------------------
># ***** BEGIN GPL LICENSE BLOCK *****
>#
># This program is free software; you can redistribute it and/or
># modify it under the terms of the GNU General Public License
># as published by the Free Software Foundation; either version 2
># of the License, or (at your option) any later version.
>#
># This program is distributed in the hope that it will be useful,
># but WITHOUT ANY WARRANTY; without even the implied warranty of
># MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
># GNU General Public License for more details.
>#
># You should have received a copy of the GNU General Public License
># along with this program; if not, write to the Free Software Foundation,
># Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
>#
># ***** END GPL LICENCE BLOCK *****
># --------------------------------------------------------------------------
>
>NULL_MAT = '(null)' # Name for mesh's that have no mat set.
>NULL_IMG = '(null)' # Name for mesh's that have no mat set.
>
>MATLIMIT = 16 # This isnt about to change but probably should not be hard coded.
>
>DIR = ''
>
>#==============================================#
># Return directory, where is file #
>#==============================================#
>def pathName(path,name):
> length=len(path)
> for CH in range(1, length):
> if path[length-CH:] == name:
> path = path[:length-CH]
> break
> return path
>
>#==============================================#
># Strips the slashes from the back of a string #
>#==============================================#
>def stripPath(path):
> for CH in range(len(path), 0, -1):
> if path[CH-1] == "/" or path[CH-1] == "\\":
> path = path[CH:]
> break
> return path
>
>#====================================================#
># Strips the prefix off the name before writing #
>#====================================================#
>def stripName(name): # name is a string
> prefixDelimiter = '.'
> return name[ : name.find(prefixDelimiter) ]
>
>
>from Blender import *
>
>#==================================================================================#
># This gets a mat or creates one of the requested name if none exist. #
>#==================================================================================#
>def getMat(matName):
> # Make a new mat
> try:
> return Material.Get(matName)
> except:
> return Material.New(matName)
>
>
>#==================================================================================#
># This function sets textures defined in .mtl file #
>#==================================================================================#
>def getImg(img_fileName):
> for i in Image.Get():
> if i.filename == img_fileName:
> return i
>
> # if we are this far it means the image hasnt been loaded.
> try:
> return Image.Load(img_fileName)
> except:
> print "unable to open", img_fileName
> return
>
>
>
>#==================================================================================#
># This function sets textures defined in .mtl file #
>#==================================================================================#
>def load_mat_image(mat, img_fileName, type, mesh):
> try:
> image = Image.Load(img_fileName)
> except:
> print "unable to open", img_fileName
> return
>
> texture = Texture.New(type)
> texture.setType('Image')
> texture.image = image
>
> # adds textures to faces (Textured/Alt-Z mode)
> # Only apply the diffuse texture to the face if the image has not been set with the inline usemat func.
> if type == 'Kd':
> for f in mesh.faces:
> if mesh.materials[f.mat].name == mat.name:
>
> # the inline usemat command overides the material Image
> if not f.image:
> f.image = image
>
> # adds textures for materials (rendering)
> if type == 'Ka':
> mat.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.CMIR)
> if type == 'Kd':
> mat.setTexture(1, texture, Texture.TexCo.UV, Texture.MapTo.COL)
> if type == 'Ks':
> mat.setTexture(2, texture, Texture.TexCo.UV, Texture.MapTo.SPEC)
>
>#==================================================================================#
># This function loads materials from .mtl file (have to be defined in obj file) #
>#==================================================================================#
>def load_mtl(dir, mtl_file, mesh):
> # Remove ./
> if mtl_file[:2] == './':
> mtl_file= mtl_file[2:]
>
> mtl_fileName = dir + mtl_file
> try:
> fileLines= open(mtl_fileName, 'r').readlines()
> except:
> print "unable to open", mtl_fileName
> return
>
> lIdx=0
> while lIdx < len(fileLines):
> l = fileLines[lIdx].split()
>
> # Detect a line that will be ignored
> if len(l) == 0:
> pass
> elif l[0] == '#' or len(l) == 0:
> pass
> elif l[0] == 'newmtl':
> currentMat = getMat(' '.join(l[1:]))
> elif l[0] == 'Ka':
> currentMat.setMirCol(float(l[1]), float(l[2]), float(l[3]))
> elif l[0] == 'Kd':
> currentMat.setRGBCol(float(l[1]), float(l[2]), float(l[3]))
> elif l[0] == 'Ks':
> currentMat.setSpecCol(float(l[1]), float(l[2]), float(l[3]))
> elif l[0] == 'Ns':
> currentMat.setHardness( int((float(l[1])*0.51)) )
> elif l[0] == 'd':
> currentMat.setAlpha(float(l[1]))
> elif l[0] == 'Tr':
> currentMat.setAlpha(float(l[1]))
> elif l[0] == 'map_Ka':
> img_fileName = dir + l[1]
> load_mat_image(currentMat, img_fileName, 'Ka', mesh)
> elif l[0] == 'map_Ks':
> img_fileName = dir + l[1]
> load_mat_image(currentMat, img_fileName, 'Ks', mesh)
> elif l[0] == 'map_Kd':
> img_fileName = dir + l[1]
> load_mat_image(currentMat, img_fileName, 'Kd', mesh)
> lIdx+=1
>
>#==================================================================================#
># This loads data from .obj file #
>#==================================================================================#
>def load_obj(file):
> time1 = sys.time()
> def applyMat(mesh, f, mat):
> # Check weather the 16 mat limit has been met.
> if len( meshList[objectName][0].materials ) >= MATLIMIT:
> print 'Warning, max material limit reached, using an existing material'
> return meshList[objectName][0]
>
> mIdx = 0
> for m in meshList[objectName][0].materials:
> if m.getName() == mat.getName():
> break
> mIdx+=1
>
> if mIdx == len(mesh.materials):
> meshList[objectName][0].addMaterial(mat)
>
> f.mat = mIdx
> return f
>
>
>
> # Get the file name with no path or .obj
> fileName = stripName( stripPath(file) )
>
> mtl_fileName = ''
>
> DIR = pathName(file, stripPath(file))
>
> fileLines = open(file, 'r').readlines()
>
> # Here we store a boolean list of which verts are used or not
> # no we know weather to add them to the current mesh
> # This is an issue with global vertex indicies being translated to per mesh indicies
> # like blenders, we start with a dummy just like the vert.
> # -1 means unused, any other value refers to the local mesh index of the vert.
>
> # objectName has a char in front of it that determins weather its a group or object.
> # We ignore it when naming the object.
> objectName = 'omesh' # If we cant get one, use this
>
> meshList = {}
> meshList[objectName] = (NMesh.GetRaw(), [-1]) # Mesh/meshList[objectName][1]
>
> uvMapList = [(0,0)] # store tuple uv pairs here
>
> # This dummy vert makes life a whole lot easier-
> # pythons index system then aligns with objs, remove later
> vertList = [NMesh.Vert(0, 0, 0)] # store tuple uv pairs here
>
> nullMat = getMat(NULL_MAT)
>
> currentMat = nullMat # Use this mat.
> currentImg = NULL_IMG # Null image is a string, otherwise this should be set to an image object.\
> currentSmooth = 0
> # Main loop
> lIdx = 0
> while lIdx < len(fileLines):
> l = fileLines[lIdx].split()
>
> # Detect a line that will be idnored
> if len(l) == 0:
> pass
> elif l[0] == '#' or len(l) == 0:
> pass
> # VERTEX
> elif l[0] == 'v':
> # This is a new vert, make a new mesh
> vertList.append( NMesh.Vert(float(l[1]), float(l[2]), float(l[3]) ) )
> meshList[objectName][1].append(-1) # Ad the moment this vert is not used by any meshList[objectName][0].
>
> elif l[0] == 'vn':
> pass
>
> elif l[0] == 'vt':
> # This is a new vert, make a new mesh
> uvMapList.append( (float(l[1]), float(l[2])) )
>
> elif l[0] == 'f':
>
> # Make a face with the correct material.
> f = NMesh.Face()
> f = applyMat(meshList[objectName][0], f, currentMat)
> f.smooth = currentSmooth
> if currentImg != NULL_IMG: f.image = currentImg
>
> # Set up vIdxLs : Verts
> # Set up vtIdxLs : UV
> # Start with a dummy objects so python accepts OBJs 1 is the first index.
> vIdxLs = []
> vtIdxLs = []
> fHasUV = len(uvMapList)-1 # Assume the face has a UV until it sho it dosent, if there are no UV coords then this will start as 0.
> for v in l[1:]:
> # OBJ files can have // or / to seperate vert/texVert/normal
> # this is a bit of a pain but we must deal with it.
> objVert = v.split('/', -1)
>
> # Vert Index - OBJ supports negative index assignment (like python)
>
> vIdxLs.append(int(objVert[0]))
> if fHasUV:
> # UV
> if len(objVert) == 1:
> vtIdxLs.append(int(objVert[0])) # Sticky UV coords
> elif objVert[1] != '': # Its possible that theres no texture vert just he vert and normal eg 1//2
> vtIdxLs.append(int(objVert[1])) # Seperate UV coords
> else:
> fHasUV = 0
>
> # Dont add a UV to the face if its larger then the UV coord list
> # The OBJ file would have to be corrupt or badly written for thi to happen
> # but account for it anyway.
> if len(vtIdxLs) > 0:
> if vtIdxLs[-1] > len(uvMapList):
> fHasUV = 0
> print 'badly written OBJ file, invalid references to UV Texture coordinates.'
>
> # Quads only, we could import quads using the method below but it polite to import a quad as a quad.
> if len(vIdxLs) == 4:
> for i in [0,1,2,3]:
> if meshList[objectName][1][vIdxLs[i]] == -1:
> meshList[objectName][0].verts.append(vertList[vIdxLs[i]])
> f.v.append(meshList[objectName][0].verts[-1])
> meshList[objectName][1][vIdxLs[i]] = len(meshList[objectName][0].verts)-1
> else:
> f.v.append(meshList[objectName][0].verts[meshList[objectName][1][vIdxLs[i]]])
>
> # UV MAPPING
> if fHasUV:
> for i in [0,1,2,3]:
> f.uv.append( uvMapList[ vtIdxLs[i] ] )
>
> if f.v > 0:
> f = applyMat(meshList[objectName][0], f, currentMat)
> if currentImg != NULL_IMG:
> f.image = currentImg
> meshList[objectName][0].faces.append(f) # move the face onto the mesh
> if len(meshList[objectName][0].faces[-1]) > 0:
> meshList[objectName][0].faces[-1].smooth = currentSmooth
>
>
> elif len(vIdxLs) >= 3: # This handles tri's and fans
> for i in range(len(vIdxLs)-2):
> f = NMesh.Face()
> f = applyMat(meshList[objectName][0], f, currentMat)
> for ii in [0, i+1, i+2]:
> if meshList[objectName][1][vIdxLs[ii]] == -1:
> meshList[objectName][0].verts.append(vertList[vIdxLs[ii]])
> f.v.append(meshList[objectName][0].verts[-1])
> meshList[objectName][1][vIdxLs[ii]] = len(meshList[objectName][0].verts)-1
> else:
> f.v.append(meshList[objectName][0].verts[meshList[objectName][1][vIdxLs[ii]]])
>
> if f.v > 0:
> f = applyMat(meshList[objectName][0], f, currentMat)
> if currentImg != NULL_IMG:
> f.image = currentImg
> meshList[objectName][0].faces.append(f) # move the face onto the mesh
> if len(meshList[objectName][0].faces[-1]) > 0:
> meshList[objectName][0].faces[-1].smooth = currentSmooth
>
>
> # UV MAPPING
> if fHasUV:
> f.uv.append( uvMapList[ vtIdxLs[0] ] )
> f.uv.append( uvMapList[ vtIdxLs[i+1] ] )
> f.uv.append( uvMapList[ vtIdxLs[i+2] ] )
>
> # Face smoothing.
> elif l[0] == 's':
> if l[1] == 'off': currentSmooth = 0
> else: currentSmooth = 1
> # print "smoothing", currentSmooth
>
> # Object / Group
> elif l[0] == 'o' or l[0] == 'g':
>
> # This makes sure that if an object and a group have the same name then
> # they are not put into the same object.
> if l[0] == 'o':
> newObjectName = 'ob_' + '_'.join(l[1:])
> elif l[0] == 'g':
> newObjectName = 'gp_' + '_'.join(l[1:])
>
> if newObjectName == '':
> objectName = 'ob_mesh'
> else:
> objectName = newObjectName
>
> # If we havnt written to this mesh before then do so.
> if objectName not in meshList.keys():
> meshList[objectName] = (NMesh.GetRaw(), [-1])
> meshList[objectName][0].verts.append( NMesh.Vert(0, 0, 0) )
>
> while len(meshList[objectName][1]) != len(vertList):
> meshList[objectName][1].append(-1)
> if len(l) > 2:
> print l
>
> # Material
> elif l[0] == 'usemtl':
> if l[1] == '(null)':
> currentMat = getMat(NULL_MAT)
> else:
> currentMat = getMat(' '.join(l[1:])) # Use join in case of spaces
>
> elif l[0] == 'usemat':
> if l[1] == '(null)':
> currentImg = NULL_IMG
> else:
> currentImg = getImg(DIR + ' '.join(l[1:])) # Use join in case of spaces
>
>
> elif l[0] == 'mtllib':
> mtl_fileName = ' '.join(l[1:])
>
> lIdx+=1
>
> #==============================================#
> # Write all meshs in the dictionary #
> #==============================================#
> for mk in meshList.keys():
> # Applies material properties to materials alredy on the mesh as well as Textures.
> if mtl_fileName != '':
> load_mtl(DIR, mtl_fileName, meshList[mk][0])
> if len(meshList[mk][0].verts) >1:
> meshList[mk][0].verts.remove(meshList[mk][0].verts[0])
> ob = NMesh.PutRaw(meshList[mk][0], mk)
> if ob.name != None:
> ob.name = mk
>
> print "obj import time: ", sys.time() - time1
>
>Window.FileSelector(load_obj, 'Import Wavefront OBJ')
>
>
>------------------------------------------------------------------------
>
>_______________________________________________
>Bf-python mailing list
>Bf-python at projects.blender.org
>http://projects.blender.org/mailman/listinfo/bf-python
>
>
--
Campbell J Barton
133 Hope Street
Geelong West, Victoria 3218 Australia
URL: http://www.metavr.com
e-mail: cbarton at metavr.com
phone: AU (03) 5229 0241
More information about the Bf-python
mailing list