ab9055d3c57ed58b1fb77972de7c501a1a5d783e
[blender.git] / release / scripts / obj_import.py
1 #!BPY
2  
3 """
4 Name: 'Wavefront (.obj)...'
5 Blender: 237
6 Group: 'Import'
7 Tooltip: 'Load a Wavefront OBJ File, Shift: batch import all dir.'
8 """
9
10 __author__= "Campbell Barton"
11 __url__= ["blender", "elysiun"]
12 __version__= "1.0"
13
14 __bpydoc__= """\
15 This script imports OBJ files to Blender.
16
17 Usage:
18
19 Run this script from "File->Import" menu and then load the desired OBJ file.
20 """
21
22 # $Id$
23 #
24 # --------------------------------------------------------------------------
25 # OBJ Import v1.0 by Campbell Barton (AKA Ideasman)
26 # --------------------------------------------------------------------------
27 # ***** BEGIN GPL LICENSE BLOCK *****
28 #
29 # This program is free software; you can redistribute it and/or
30 # modify it under the terms of the GNU General Public License
31 # as published by the Free Software Foundation; either version 2
32 # of the License, or (at your option) any later version.
33 #
34 # This program is distributed in the hope that it will be useful,
35 # but WITHOUT ANY WARRANTY; without even the implied warranty of
36 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37 # GNU General Public License for more details.
38 #
39 # You should have received a copy of the GNU General Public License
40 # along with this program; if not, write to the Free Software Foundation,
41 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
42 #
43 # ***** END GPL LICENCE BLOCK *****
44 # --------------------------------------------------------------------------
45
46
47 #==============================================#
48 # Return directory, where the file is          #
49 #==============================================#
50 def stripFile(path):
51         lastSlash= max(path.rfind('\\'), path.rfind('/'))
52         if lastSlash != -1:
53                 path= path[:lastSlash]
54         return '%s%s' % (path, sys.sep)
55
56 #==============================================#
57 # Strips the slashes from the back of a string #
58 #==============================================#
59 def stripPath(path):
60         return path.split('/')[-1].split('\\')[-1]
61         
62 #====================================================#
63 # Strips the prefix off the name before writing      #
64 #====================================================#
65 def stripExt(name): # name is a string
66         index= name.rfind('.')
67         if index != -1:
68                 return name[ : index ]
69         else:
70                 return name
71
72
73 from Blender import *
74 import BPyImage
75 reload(BPyImage)
76 import BPyMesh
77
78 try:
79         import os
80 except:
81         # So we know if os exists.
82         print 'Module "os" not found, install python to enable comprehensive image finding and batch loading.'
83         os= None
84
85
86 #==================================================================================#
87 # This function sets textures defined in .mtl file                                 #
88 #==================================================================================#
89 def loadMaterialImage(mat, img_fileName, type, meshDict, dir):
90         TEX_ON_FLAG= NMesh.FaceModes['TEX']
91         
92         texture= Texture.New(type)
93         texture.setType('Image')
94         
95         # Absolute path - c:\.. etc would work here
96         image= BPyImage.comprehensiveImageLoad(img_fileName, dir)
97         
98         if image:
99                 texture.image= image
100                 
101         # adds textures to faces (Textured/Alt-Z mode)
102         # Only apply the diffuse texture to the face if the image has not been set with the inline usemat func.
103         if image and type == 'Kd':
104                 for meshPair in meshDict.itervalues():
105                         for f in meshPair[0].faces:
106                                 #print meshPair[0].materials[f.mat].name, mat.name
107                                 if meshPair[0].materials[f.mat].name == mat.name:
108                                         # the inline usemat command overides the material Image
109                                         if not f.image:
110                                                 f.mode |= TEX_ON_FLAG
111                                                 f.image= image
112         
113         # adds textures for materials (rendering)
114         elif type == 'Ka':
115                 mat.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.CMIR) # TODO- Add AMB to BPY API
116         elif type == 'Kd':
117                 mat.setTexture(1, texture, Texture.TexCo.UV, Texture.MapTo.COL)
118         elif type == 'Ks':
119                 mat.setTexture(2, texture, Texture.TexCo.UV, Texture.MapTo.SPEC)
120         
121         elif type == 'Bump': # New Additions
122                 mat.setTexture(3, texture, Texture.TexCo.UV, Texture.MapTo.NOR)         
123         elif type == 'D':
124                 mat.setTexture(4, texture, Texture.TexCo.UV, Texture.MapTo.ALPHA)                               
125         elif type == 'refl':
126                 mat.setTexture(5, texture, Texture.TexCo.UV, Texture.MapTo.REF)                         
127         
128
129 #==================================================================================#
130 # This function loads materials from .mtl file (have to be defined in obj file)    #
131 #==================================================================================#
132 def load_mtl(dir, mtl_file, meshDict, materialDict):
133         
134         #===============================================================================#
135         # This gets a mat or creates one of the requested name if none exist.           #
136         #===============================================================================#
137         def getMat(matName, materialDict):
138                 # Make a new mat
139                 try:
140                         return materialDict[matName]
141                 #except NameError or KeyError:
142                 except: # Better do any exception
143                         # Do we realy need to keep the dict up to date?, not realy but keeps consuistant.
144                         mat= materialDict[matName]= Material.New(matName)
145                         return mat
146                 
147                         
148         mtl_file= stripPath(mtl_file)
149         mtl_fileName= dir + mtl_file
150         
151         try:
152                 fileLines= open(mtl_fileName, 'r').readlines()
153         except IOError:
154                 print '\tunable to open referenced material file: "%s"' % mtl_fileName
155                 return
156         
157         try:
158                 lIdx=0
159                 while lIdx < len(fileLines):
160                         l= fileLines[lIdx].split()
161                         
162                         # Detect a line that will be ignored
163                         if len(l) == 0 or l[0].startswith('#'):
164                                 pass
165                         elif l[0] == 'newmtl':
166                                 currentMat= getMat('_'.join(l[1:]), materialDict) # Material should alredy exist.
167                         elif l[0] == 'Ka':
168                                 currentMat.setMirCol((float(l[1]), float(l[2]), float(l[3])))
169                         elif l[0] == 'Kd':
170                                 currentMat.setRGBCol((float(l[1]), float(l[2]), float(l[3])))
171                         elif l[0] == 'Ks':
172                                 currentMat.setSpecCol((float(l[1]), float(l[2]), float(l[3])))
173                         elif l[0] == 'Ns':
174                                 currentMat.setHardness( int((float(l[1])*0.51)) )
175                         elif l[0] == 'Ni': # Refraction index
176                                 currentMat.setIOR( max(1, min(float(l[1]), 3))) # Between 1 and 3
177                         elif l[0] == 'd':
178                                 currentMat.setAlpha(float(l[1]))
179                         elif l[0] == 'Tr':
180                                 currentMat.setAlpha(float(l[1]))
181                         elif l[0] == 'map_Ka':
182                                 img_fileName= ' '.join(l[1:])
183                                 loadMaterialImage(currentMat, img_fileName, 'Ka', meshDict, dir)
184                         elif l[0] == 'map_Ks':
185                                 img_fileName= ' '.join(l[1:])
186                                 loadMaterialImage(currentMat, img_fileName, 'Ks', meshDict, dir)
187                         elif l[0] == 'map_Kd':
188                                 img_fileName= ' '.join(l[1:])
189                                 loadMaterialImage(currentMat, img_fileName, 'Kd', meshDict, dir)
190                         
191                         # new additions
192                         elif l[0] == 'map_Bump': # Bumpmap
193                                 img_fileName= ' '.join(l[1:])                   
194                                 loadMaterialImage(currentMat, img_fileName, 'Bump', meshDict, dir)
195                         elif l[0] == 'map_D': # Alpha map - Dissolve
196                                 img_fileName= ' '.join(l[1:])                   
197                                 loadMaterialImage(currentMat, img_fileName, 'D', meshDict, dir)
198
199                         elif l[0] == 'refl': # Reflectionmap
200                                 img_fileName= ' '.join(l[1:])                   
201                                 loadMaterialImage(currentMat, img_fileName, 'refl', meshDict, dir)
202                         
203                         lIdx+=1
204         except:
205                 print '\tERROR: Unable to parse MTL file: "%s"' % mtl_file
206                 return
207         print '\tUsing MTL: "%s"' % mtl_file
208 #===========================================================================#
209 # Returns unique name of object/mesh (preserve overwriting existing meshes) #
210 #===========================================================================#
211 def getUniqueName(name):
212         newName= name[:19] # 19 chars is the longest name.
213         uniqueInt= 0
214         while newName in getUniqueName.uniqueNames:
215                 newName= '%s.%.3i' % (name[:15], uniqueInt)
216                 uniqueInt +=1
217         getUniqueName.uniqueNames.append(newName)
218         return newName
219 getUniqueName.uniqueNames= []
220
221 #==================================================================================#
222 # This loads data from .obj file                                                   #
223 #==================================================================================#
224 def load_obj(file, IMPORT_MTL=1, IMPORT_EDGES=1, IMPORT_SMOOTH_ALL=0, IMPORT_FGON=1, IMPORT_SMOOTH_GROUPS=0, IMPORT_MTL_SPLIT=0, IMPORT_RELATIVE_VERTS=0):
225         global currentMesh,\
226         currentUsedVertList,\
227         currentUsedVertListSmoothGroup,\
228         meshDict,\
229         contextMeshMatIdx,\
230         currentMaterialMeshMapping
231         
232         print '\nImporting OBJ file: "%s"' % file
233         
234         time1= sys.time()
235         
236         getUniqueName.uniqueNames.extend( [ob.name for ob in Object.Get()] )
237         getUniqueName.uniqueNames.extend( NMesh.GetNames() )
238         
239         # Deselect all objects in the scene.
240         # do this first so we dont have to bother, with objects we import
241         for ob in Scene.GetCurrent().getChildren():
242                 ob.sel= 0
243         
244         TEX_OFF_FLAG= ~NMesh.FaceModes['TEX']
245         
246         # Get the file name with no path or .obj
247         fileName= stripExt( stripPath(file) )
248
249         mtl_fileName= [] # Support multiple mtl files if needed.
250
251         DIR= stripFile(file)
252         
253         tempFile= open(file, 'r')
254         fileLines= tempFile.readlines() 
255         tempFile.close()
256         del tempFile
257         uvMapList= [] # store tuple uv pairs here
258         
259         # This dummy vert makes life a whole lot easier-
260         # pythons index system then aligns with objs, remove later
261         vertList= [] # Could havea vert but since this is a placeholder theres no Point
262         
263         
264         # Store all imported images in a dict, names are key
265         imageDict= {}
266         
267         # This stores the index that the current mesh has for the current material.
268         # if the mesh does not have the material then set -1
269         contextMeshMatIdx= -1
270         
271         # Keep this out of the dict for easy accsess.
272         nullMat= Material.New('(null)')
273         
274         currentMat= nullMat # Use this mat.
275         currentImg= None # Null image is a string, otherwise this should be set to an image object.\
276         if IMPORT_SMOOTH_ALL:
277                 currentSmooth= True
278         else:
279                 currentSmooth= False
280         
281         # Store a list of unnamed names
282         currentUnnamedGroupIdx= 1
283         currentUnnamedObjectIdx= 1
284         
285         quadList= (0, 1, 2, 3)
286         
287         faceQuadVList= [None, None, None, None]
288         faceTriVList= [None, None, None]
289         
290         #==================================================================================#
291         # Load all verts first (texture verts too)                                         #
292         #==================================================================================#
293         print '\tfile length: %d' % len(fileLines)
294         # Ignore normals and comments.
295         fileLines= [lsplit for l in fileLines if not l.startswith('vn') if not l.startswith('#') for lsplit in (l.split(),) if lsplit]
296         Vert= NMesh.Vert
297         try:
298                 vertList= [Vert(float(l[1]), float(l[2]), float(l[3]) ) for l in fileLines if l[0] == 'v']
299         except ValueError:
300                 # What??? Maya 7 uses "6,45" instead of "6.45"
301                 vertList= [Vert(float(l[1].replace(',', '.')), float(l[2].replace(',', '.')), float(l[3].replace(',', '.')) ) for l in fileLines if l[0] == 'v']
302                 
303         try:
304                 uvMapList= [(float(l[1]), float(l[2])) for l in fileLines if l[0] == 'vt']
305         except ValueError:
306                 # Same amazement as above. call that a float?
307                 uvMapList= [(float(l[1].replace(',', '.')), float(l[2].replace(',', '.'))) for l in fileLines if l[0] == 'vt']
308                 
309         if IMPORT_SMOOTH_GROUPS:
310                 smoothingGroups=  dict([('_'.join(l[1:]), None) for l in fileLines if l[0] == 's' ])
311         else:
312                 smoothingGroups= {}
313         materialDict=     dict([('_'.join(l[1:]), None) for l in fileLines if l[0] == 'usemtl']) # Store all imported materials as unique dict, names are key
314         print '\tvert:%i  texverts:%i  smoothgroups:%i  materials:%s' % (len(vertList), len(uvMapList), len(smoothingGroups), len(materialDict))
315         
316         # Replace filelines, Excluding v excludes "v ", "vn " and "vt "
317         # Remove any variables we may have created.
318         try: del _dummy
319         except: pass
320         try: del _x
321         except: pass
322         try: del _y
323         except: pass
324         try: del _z
325         except: pass
326         try: del lsplit
327         except: pass
328         del Vert
329         
330         
331         # With negative values this is used a lot. make faster access.
332         len_uvMapList= len(uvMapList)
333         len_vertList= len(vertList)
334         
335         #  Only want unique keys anyway
336         smoothingGroups['(null)']= None # Make sure we have at least 1.
337         smoothingGroups= smoothingGroups.keys()
338         print '\tfound %d smoothing groups.' % (len(smoothingGroups) -1)
339         
340         # Add materials to Blender for later is in teh OBJ
341         for k in materialDict.iterkeys():
342                 materialDict[k]= Material.New(k)
343         
344         
345         # Make a list of all unused vert indices that we can copy from
346         VERT_USED_LIST= [-1]*len_vertList
347         
348         # Here we store a boolean list of which verts are used or not
349         # no we know weather to add them to the current mesh
350         # This is an issue with global vertex indices being translated to per mesh indices
351         # like blenders, we start with a dummy just like the vert.
352         # -1 means unused, any other value refers to the local mesh index of the vert.
353
354         # currentObjectName has a char in front of it that determins weather its a group or object.
355         # We ignore it when naming the object.
356         currentObjectName= 'unnamed_obj_0' # If we cant get one, use this
357         
358         if IMPORT_MTL_SPLIT:
359                 currentObjectName_real= currentObjectName
360         
361         #meshDict= {} # The 3 variables below are stored in a tuple within this dict for each mesh
362         currentMesh= NMesh.GetRaw() # The NMesh representation of the OBJ group/Object
363         #currentUsedVertList= {} # A Dict of smooth groups, each smooth group has a list of used verts and they are generated on demand so as to save memory.
364         currentMaterialMeshMapping= {} # Used to store material indices so we dont have to search the mesh for materials every time.
365         
366         # Every mesh has a null smooth group, this is used if there are no smooth groups in the OBJ file.
367         # and when for faces where no smooth group is used.
368         currentSmoothGroup= '(null)' # The Name of the current smooth group
369         
370         # For direct accsess to the Current Meshes, Current Smooth Groups- Used verts.
371         # This is of course context based and changes on the fly.
372         # Set the initial '(null)' Smooth group, every mesh has one.
373         currentUsedVertListSmoothGroup= VERT_USED_LIST[:]
374         currentUsedVertList= {currentSmoothGroup: currentUsedVertListSmoothGroup }
375         
376         # 0:NMesh, 1:SmoothGroups[UsedVerts[0,0,0,0]], 2:materialMapping['matname':matIndexForThisNMesh]
377         meshDict= {currentObjectName: (currentMesh, currentUsedVertList, currentMaterialMeshMapping) }
378         
379         # Only show the bad uv error once 
380         badObjUvs= 0
381         badObjFaceVerts= 0
382         badObjFaceTexCo= 0
383         
384         
385         #currentMesh.verts.append(vertList[0]) # So we can sync with OBJ indices where 1 is the first item.
386         if len_uvMapList > 1:
387                 currentMesh.hasFaceUV(1) # Turn UV's on if we have ANY texture coords in this obj file.
388         
389         
390         
391         # Heres the code that gets a mesh, creating a new one if needed.
392         # may_exist is used to avoid a dict looup.
393         # if the mesh is unnamed then we generate a new name and dont bother looking
394         # to see if its alredy there.
395         def obj_getmesh(may_exist):
396                 global currentMesh,\
397                 currentUsedVertList,\
398                 currentUsedVertListSmoothGroup,\
399                 meshDict,\
400                 contextMeshMatIdx,\
401                 currentMaterialMeshMapping
402                 
403                 #print 'getting mesh,', currentObjectName
404                 
405                 # If we havnt written to this mesh before then do so.
406                 # if we have then we'll just keep appending to it, this is required for soem files.
407                 
408                 # If we are new, or we are not yet in the list of added meshes
409                 # then make us new mesh.
410                 if (not may_exist) or (not meshDict.has_key(currentObjectName)):
411                         currentMesh= NMesh.GetRaw()
412                         
413                         currentUsedVertList= {}
414                         
415                         # SmoothGroup is a string
416                         ########currentSmoothGroup= '(null)' # From examplesm changing the g/o shouldent change the smooth group.
417                         currentUsedVertList[currentSmoothGroup]= currentUsedVertListSmoothGroup= VERT_USED_LIST[:]                                              
418                         
419                         currentMaterialMeshMapping= {}
420                         meshDict[currentObjectName]= (currentMesh, currentUsedVertList, currentMaterialMeshMapping)
421                         currentMesh.hasFaceUV(1)
422                         contextMeshMatIdx= -1
423                         
424                 else: 
425                         # Since we have this in Blender then we will check if the current Mesh has the material.
426                         # set the contextMeshMatIdx to the meshs index but only if we have it.
427                         currentMesh, currentUsedVertList, currentMaterialMeshMapping= meshDict[currentObjectName]
428                         #getMeshMaterialIndex(currentMesh, currentMat)
429                         
430                         try:
431                                 contextMeshMatIdx= currentMaterialMeshMapping[currentMat.name] #getMeshMaterialIndex(currentMesh, currentMat)
432                         except KeyError:
433                                 contextMeshMatIdx -1
434                         
435                         # For new meshes switch smoothing groups to null
436                         ########currentSmoothGroup= '(null)'  # From examplesm changing the g/o shouldent change the smooth group.
437                         try:
438                                 currentUsedVertListSmoothGroup= currentUsedVertList[currentSmoothGroup]
439                         except:
440                                 currentUsedVertList[currentSmoothGroup]= currentUsedVertListSmoothGroup= VERT_USED_LIST[:]                                              
441                 
442         
443         
444         
445         
446         
447         
448         #==================================================================================#
449         # Load all faces into objects, main loop                                           #
450         #==================================================================================#
451         lIdx= 0
452         EDGE_FGON_FLAG= NMesh.EdgeFlags['FGON']
453         EDGE_DRAW_FLAG= NMesh.EdgeFlags['EDGEDRAW']
454         
455         if IMPORT_RELATIVE_VERTS:
456                 VERT_COUNT= TVERT_COUNT=0
457                 # TOT_VERTS= len(vertList) 
458                 # len(vertList)  
459                 # len(uvMapList)
460         
461         while lIdx < len(fileLines):
462                 l= fileLines[lIdx]
463                 #for l in fileLines:
464                 if len(l) == 0:
465                         continue
466                 # FACE
467                 elif l[0] == 'v':
468                         if IMPORT_RELATIVE_VERTS:
469                                 VERT_COUNT+=1
470                 elif l[0] == 'vt':
471                         if IMPORT_RELATIVE_VERTS:
472                                 TVERT_COUNT+=1
473                 
474                 elif l[0] == 'f' or l[0] == 'fo': # fo is not standard.
475                         # Make a face with the correct material.
476                         
477                         # Add material to mesh
478                         if contextMeshMatIdx == -1:
479                                 tmpMatLs= currentMesh.materials
480                                 
481                                 if len(tmpMatLs) == 16:
482                                         contextMeshMatIdx= 0 # Use first material
483                                         print 'material overflow, attempting to use > 16 materials. defaulting to first.'
484                                 else:
485                                         contextMeshMatIdx= len(tmpMatLs)
486                                         currentMaterialMeshMapping[currentMat.name]= contextMeshMatIdx
487                                         currentMesh.addMaterial(currentMat)
488                         
489                         # Set up vIdxLs : Verts
490                         # Set up vtIdxLs : UV
491                         # Start with a dummy objects so python accepts OBJs 1 is the first index.
492                         vIdxLs= []
493                         vtIdxLs= []
494                         
495                         
496                         fHasUV= len_uvMapList # Assume the face has a UV until it sho it dosent, if there are no UV coords then this will start as 0.
497                         
498                         # Support stupid multiline faces
499                         # not an obj spec but some objs exist that do this.
500                         # f 1 2 3 \ 
501                         #   4 5 6 \
502                         # ..... instead of the more common and sane.
503                         # f 1 2 3 4 5 6
504                         #
505                         # later lines are not modified, just skepped by advancing "lIdx"
506                         while l[-1] == '\\':
507                                 l.pop()
508                                 lIdx+=1
509                                 l.extend(fileLines[lIdx])
510                         # Done supporting crappy obj faces over multiple lines.
511                         
512                         for v in l:
513                                 if v is not 'f': # Only the first v will be f, any better ways to skip it?
514                                         # OBJ files can have // or / to seperate vert/texVert/normal
515                                         # this is a bit of a pain but we must deal with it.
516                                         objVert= v.split('/')
517                                         
518                                         # Vert Index - OBJ supports negative index assignment (like python)
519                                         index= int(objVert[0])-1
520                                         # Account for negative indices.
521                                         if index < 0:
522                                                 if IMPORT_RELATIVE_VERTS: # non standard
523                                                         index= VERT_COUNT+index+1
524                                                 else:
525                                                         index= len_vertList+index+1
526                                         elif IMPORT_RELATIVE_VERTS:
527                                                 index= VERT_COUNT+index+1 # UNTESTED, POSITIVE RELATIVE VERTS.May be out by 1.
528                                         
529                                         vIdxLs.append(index)
530                                         if fHasUV:
531                                                 # UV
532                                                 index= 0 # Dummy var
533                                                 if len(objVert) == 1:
534                                                         index= vIdxLs[-1]
535                                                 elif objVert[1]: # != '' # Its possible that theres no texture vert just he vert and normal eg 1//2
536                                                         index= int(objVert[1])-1
537                                                         if index < 0:
538                                                                 if IMPORT_RELATIVE_VERTS: # non standard
539                                                                         index= TVERT_COUNT+index+1
540                                                                 else:
541                                                                         index= len_uvMapList+index+1
542                                                         elif IMPORT_RELATIVE_VERTS: # non standard:
543                                                                 index= TVERT_COUNT+index+1 # UNTESTED, POSITIVE RELATIVE VERTS. May be out by 1.
544                                                         
545                                                 if len_uvMapList > index:
546                                                         vtIdxLs.append(index) # Seperate UV coords
547                                                 else:
548                                                         # BAD FILE, I have found this so I account for it.
549                                                         # INVALID UV COORD
550                                                         # Could ignore this- only happens with 1 in 1000 files.
551                                                         badObjFaceTexCo +=1
552                                                         vtIdxLs.append(1)
553                                                         
554                                                         fHasUV= 0
555                 
556                                                 # Dont add a UV to the face if its larger then the UV coord list
557                                                 # The OBJ file would have to be corrupt or badly written for thi to happen
558                                                 # but account for it anyway.
559                                                 if len(vtIdxLs) > 0:
560                                                         if vtIdxLs[-1] > len_uvMapList:
561                                                                 fHasUV= 0
562                                                                 
563                                                                 badObjUvs +=1 # ERROR, Cont
564                         
565                         # Quads only, we could import quads using the method below but it polite to import a quad as a quad.
566                         #print lIdx, len(vIdxLs), len(currentUsedVertListSmoothGroup)
567                         #print fileLines[lIdx]
568                         
569                         # Add all the verts we need,
570                         # dont edd edge verts if were not importing them.
571                         face_vert_count= len(vIdxLs)
572                         if (not IMPORT_EDGES) and face_vert_count == 2:
573                                 pass
574                         else:
575                                 # Add the verts that arnt alredy added.
576                                 for i in vIdxLs:
577                                         if currentUsedVertListSmoothGroup[i] == -1:
578                                                 v= vertList[i]
579                                                 currentMesh.verts.append(v)
580                                                 currentUsedVertListSmoothGroup[i]= len(currentMesh.verts)-1
581                         
582                         if face_vert_count == 2:
583                                 if IMPORT_EDGES and vIdxLs[0]!=vIdxLs[1]:
584                                         # Edge
585                                         currentMesh.addEdge(\
586                                         currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[0]]],\
587                                         currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[0]]]) 
588                                         
589                         elif face_vert_count == 4:
590                                 
591                                 # Have found some files where wach face references the same vert
592                                 # - This causes a bug and stopts the import so lets check here
593                                 if vIdxLs[0] == vIdxLs[1] or\
594                                 vIdxLs[0] == vIdxLs[2] or\
595                                 vIdxLs[0] == vIdxLs[3] or\
596                                 vIdxLs[1] == vIdxLs[2] or\
597                                 vIdxLs[1] == vIdxLs[3] or\
598                                 vIdxLs[2] == vIdxLs[3]:
599                                         badObjFaceVerts+=1
600                                 else:
601                                         for i in quadList: #  quadList == [0,1,2,3] 
602                                                 faceQuadVList[i]= currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[i]]]
603                                         
604                                         f= NMesh.Face(faceQuadVList)
605                                         # UV MAPPING
606                                         if fHasUV:
607                                                 f.uv= [uvMapList[ vtIdxLs[0] ],uvMapList[ vtIdxLs[1] ],uvMapList[ vtIdxLs[2] ],uvMapList[ vtIdxLs[3] ]]
608                                                 if currentImg:
609                                                         f.image= currentImg
610                                                 else:
611                                                         f.mode &= TEX_OFF_FLAG
612                                         
613                                         f.mat= contextMeshMatIdx
614                                         f.smooth= currentSmooth
615                                         currentMesh.faces.append(f) # move the face onto the mesh
616                         
617                         elif face_vert_count == 3: # This handles tri's and fans, dont use fans anymore.
618                                 for i in range(face_vert_count-2):
619                                         if vIdxLs[0] == vIdxLs[i+1] or\
620                                         vIdxLs[0] == vIdxLs[i+2] or\
621                                         vIdxLs[i+1] == vIdxLs[i+2]:
622                                                 badObjFaceVerts+=1
623                                         else:
624                                                 for k, j in [(0,0), (1,i+1), (2,i+2)]:
625                                                         faceTriVList[k]= currentMesh.verts[currentUsedVertListSmoothGroup[vIdxLs[j]]]   
626                                                 
627                                                 f= NMesh.Face(faceTriVList)     
628                                                 
629                                                 # UV MAPPING
630                                                 if fHasUV:
631                                                         f.uv= [uvMapList[vtIdxLs[0]], uvMapList[vtIdxLs[i+1]], uvMapList[vtIdxLs[i+2]]]
632                                                         if currentImg:
633                                                                 f.image= currentImg
634                                                         else:
635                                                                 f.mode &= TEX_OFF_FLAG
636                                                 
637                                                 f.mat= contextMeshMatIdx
638                                                 f.smooth= currentSmooth
639                                                 currentMesh.faces.append(f) # move the face onto the mesh
640                         
641                         elif face_vert_count > 4: # NGons.
642                                 # we need to map indices to uv coords.
643                                 currentMeshRelativeIdxs= [currentUsedVertListSmoothGroup[i] for i in vIdxLs]
644                                 
645                                 if fHasUV:
646                                         vert2UvMapping=dict( [ (currentMeshRelativeIdxs[i],vtIdxLs[i]) for i in xrange(face_vert_count)] )
647                                 
648                                 ngon_face_indices= BPyMesh.ngon(currentMesh, currentMeshRelativeIdxs)
649                                 
650                                 
651                                 # At the moment scanfill always makes tri's but dont count on it
652                                 for fillFace in ngon_face_indices:
653                                         f= NMesh.Face([currentMesh.verts[currentMeshRelativeIdxs[i]] for i in fillFace])
654                                         
655                                         if fHasUV:
656                                                 f.uv= [uvMapList[vert2UvMapping[currentMeshRelativeIdxs[i]]] for i in fillFace]
657                                                 if currentImg:
658                                                         f.image= currentImg
659                                                 else:
660                                                         f.mode &= TEX_OFF_FLAG
661                                         
662                                         f.mat= contextMeshMatIdx
663                                         f.smooth= currentSmooth
664                                         currentMesh.faces.append(f) # move the face onto the mesh
665                                 
666                                 # Set fgon flag.
667                                 if IMPORT_FGON:
668                                         edgeUsers={}
669                                         for fillFace in ngon_face_indices:
670                                                 for i in xrange(len(fillFace)): # Should always be 3
671                                                         i1= currentMeshRelativeIdxs[fillFace[i]]
672                                                         i2= currentMeshRelativeIdxs[fillFace[i-1]]
673                                                         
674                                                         # Sort the pair so thet always match.
675                                                         if i1>i2: i1,i2=i2,i1
676                                                                 
677                                                         try:
678                                                                 edgeUsers[i1,i2]+= 1
679                                                         except:
680                                                                 edgeUsers[i1,i2]= 0
681                                         
682                                         for edgeVerts, users in edgeUsers.iteritems():
683                                                 if users:
684                                                         ed= currentMesh.addEdge(\
685                                                          currentMesh.verts[edgeVerts[0]],\
686                                                          currentMesh.verts[edgeVerts[1]])
687                                                         
688                                                         ed.flag|= EDGE_FGON_FLAG
689                         
690                 # FACE SMOOTHING
691                 elif l[0] == 's' and IMPORT_SMOOTH_GROUPS:
692                         # No value? then turn on.
693                         if len(l) == 1:
694                                 currentSmooth= True
695                                 currentSmoothGroup= '(null)'
696                         else:
697                                 if l[1] == 'off': # We all have a null group so dont need to try, will try anyway to avoid code duplication.
698                                         if not IMPORT_SMOOTH_ALL:
699                                                 currentSmooth= False
700                                         currentSmoothGroup= '(null)'
701                                 else: 
702                                         currentSmooth= True
703                                         currentSmoothGroup= '_'.join(l[1:])
704                         try:
705                                 currentUsedVertListSmoothGroup= currentUsedVertList[currentSmoothGroup]
706                         except KeyError:
707                                 currentUsedVertList[currentSmoothGroup]= currentUsedVertListSmoothGroup= VERT_USED_LIST[:]
708                 
709                 
710                 # OBJECT / GROUP
711                 elif l[0] == 'o' or l[0] == 'g':
712                         
713                         # Forget about the current image
714                         currentImg= None
715                         
716                         # This makes sure that if an object and a group have the same name then
717                         # they are not put into the same object.
718                         
719                         # Only make a new group.object name if the verts in the existing object have been used, this is obscure
720                         # but some files face groups seperating verts and faces which results in silly things. (no groups have names.)
721                         if len(l) == 1:
722                                 # Make a new empty name
723                                 if l[0] == 'g': # Make a blank group name
724                                         currentObjectName= 'unnamed_grp_%.4d' % currentUnnamedGroupIdx
725                                         currentUnnamedGroupIdx +=1
726                                 else: # is an object.
727                                         currentObjectName= 'unnamed_ob_%.4d' % currentUnnamedObjectIdx
728                                         currentUnnamedObjectIdx +=1
729                                 may_exist= False # we know the model is new.
730                         else: # No name given
731                                 currentObjectName= '_'.join(l[1:])
732                                 may_exist= True
733                         
734                         if IMPORT_MTL_SPLIT:
735                                 currentObjectName_real= currentObjectName
736                                 currentObjectName += '_'+currentMat.name
737                         
738                         obj_getmesh(may_exist)
739                                 
740                 
741                 # MATERIAL
742                 elif l[0] == 'usemtl':
743                         if len(l) == 1 or l[1] == '(null)':
744                                 currentMat= nullMat # We know we have a null mat.
745                         else:
746                                 currentMat= materialDict['_'.join(l[1:])]
747                                 try:
748                                         contextMeshMatIdx= currentMaterialMeshMapping[currentMat.name]
749                                 except KeyError:
750                                         contextMeshMatIdx= -1 #getMeshMaterialIndex(currentMesh, currentMat)
751                         
752                         # Check if we are splitting by material.
753                         if IMPORT_MTL_SPLIT:
754                                 currentObjectName= currentObjectName_real+'_'+currentMat.name
755                                 obj_getmesh(True)
756                         
757                         
758                 # IMAGE
759                 elif l[0] == 'usemat' or l[0] == 'usemap':
760                         if len(l) == 1 or l[1] == '(null)' or l[1] == 'off':
761                                 currentImg= None
762                         else:
763                                 # Load an image.
764                                 newImgName= stripPath(' '.join(l[1:])) # Use space since its a file name.
765                                 
766                                 try:
767                                         # Assume its alredy set in the dict (may or maynot be loaded)
768                                         currentImg= imageDict[newImgName]
769                                 
770                                 except KeyError: # Not in dict, add for first time.
771                                         # Image has not been added, Try and load the image
772                                         currentImg= BPyImage.comprehensiveImageLoad(newImgName, DIR) # Use join in case of spaces 
773                                         imageDict[newImgName]= currentImg
774                                         # These may be None, thats okay.
775                 
776                 # MATERIAL FILE
777                 elif l[0] == 'mtllib' and IMPORT_MTL and len(l)>1:
778                         mtl_fileName.append(' '.join(l[1:]) ) # Support for multiple MTL's
779                 lIdx+=1
780                 
781         # Applies material properties to materials alredy on the mesh as well as Textures.
782         if IMPORT_MTL:
783                 for mtl in mtl_fileName:
784                         load_mtl(DIR, mtl, meshDict, materialDict)      
785         
786         importedObjects= []
787         for mk, me in meshDict.iteritems():
788                 nme= me[0]
789                 
790                 # Ignore no vert meshes.
791                 if not nme.verts: # == []
792                         continue
793                 name= getUniqueName(mk)
794                 ob= NMesh.PutRaw(nme, name)
795                 ob.name= name
796                 
797                 importedObjects.append(ob)
798         
799         # Select all imported objects.
800         for ob in importedObjects:
801                 ob.sel= 1
802         if badObjUvs > 0:
803                 print '\tERROR: found %d faces with badly formatted UV coords. everything else went okay.' % badObjUvs
804         
805         if badObjFaceVerts > 0:
806                 print '\tERROR: found %d faces reusing the same vertex. everything else went okay.' % badObjFaceVerts
807         
808         if badObjFaceTexCo > 0:
809                 print '\tERROR: found %d faces with invalit texture coords. everything else went okay.' % badObjFaceTexCo               
810         
811         
812         print "obj import time: ", sys.time() - time1
813
814 def load_obj_ui(file):
815         
816         IMPORT_MTL= Draw.Create(1)
817         IMPORT_DIR= Draw.Create(0)
818         IMPORT_NEW_SCENE= Draw.Create(0)
819         IMPORT_EDGES= Draw.Create(1)
820         IMPORT_SMOOTH_ALL= Draw.Create(1)
821         IMPORT_FGON= Draw.Create(1)
822         IMPORT_SMOOTH_GROUPS= Draw.Create(0)
823         IMPORT_MTL_SPLIT= Draw.Create(0)
824         IMPORT_RELATIVE_VERTS= Draw.Create(0)
825         
826         # Get USER Options
827         pup_block= [\
828         ('Material (*.mtl)', IMPORT_MTL, 'Imports material settings and images from the obj\'s .mtl file'),\
829         ('All *.obj\'s in dir', IMPORT_DIR, 'Import all obj files in this dir (avoid overlapping data with "Create scene")'),\
830         ('Create scene', IMPORT_NEW_SCENE, 'Imports each obj into its own scene, named from the file'),\
831         'Geometry...',\
832         ('Edges', IMPORT_EDGES, 'Import faces with 2 verts as in edge'),\
833         ('Smooths all faces', IMPORT_SMOOTH_ALL, 'Smooth all faces even if they are not in a smoothing group'),\
834         ('Create FGons', IMPORT_FGON, 'Import faces with more then 4 verts as fgons.'),\
835         ('Smooth Groups', IMPORT_SMOOTH_GROUPS, 'Only Share verts within smooth groups. (Warning, Hogs Memory)'),\
836         ('Split by Material', IMPORT_MTL_SPLIT, 'Import each material into a seperate mesh (Avoids >16 meterials per mesh problem)'),\
837         ('Relative Verts', IMPORT_RELATIVE_VERTS, 'Import non standard OBJs with relative vertex indices, try if your mesh imports with scrambled faces.'),\
838         ]
839         
840         if not os:
841                 pup_block.pop(2) # Make sure this is the IMPORT_DIR option that requires OS
842         
843         if not Draw.PupBlock('Import...', pup_block):
844                 return
845         
846         Window.WaitCursor(1)
847         Window.DrawProgressBar(0, '')
848         time= sys.time()
849         
850         IMPORT_MTL= IMPORT_MTL.val
851         IMPORT_DIR= IMPORT_DIR.val
852         IMPORT_NEW_SCENE= IMPORT_NEW_SCENE.val
853         IMPORT_EDGES= IMPORT_EDGES.val
854         IMPORT_SMOOTH_ALL= IMPORT_SMOOTH_ALL.val
855         IMPORT_FGON= IMPORT_FGON.val
856         IMPORT_SMOOTH_GROUPS= IMPORT_SMOOTH_GROUPS.val
857         IMPORT_MTL_SPLIT= IMPORT_MTL_SPLIT.val
858         IMPORT_RELATIVE_VERTS= IMPORT_RELATIVE_VERTS.val
859         #orig_scene= Scene.GetCurrent()
860         
861         obj_dir= stripFile(file)
862         if IMPORT_DIR:
863                 obj_files= [(obj_dir,f) for f in os.listdir(obj_dir) if f.lower().endswith('obj')]
864         else:
865                 obj_files= [(obj_dir,stripPath(file))]
866         
867         obj_len= len(obj_files)
868         count= 0
869         for d, f in obj_files:
870                 count+= 1
871                 if not sys.exists(d+f):
872                         print 'Error: "%s%s" does not exist' % (d,f)
873                 else:
874                         if IMPORT_NEW_SCENE:
875                                 scn= Scene.New('.'.join(f.split('.')[0:-1]))
876                                 scn.makeCurrent()
877                         
878                         
879                         Window.DrawProgressBar((float(count)/obj_len) - 0.01, '%s: %i of %i' % (f, count, obj_len))
880                         load_obj(d+f, IMPORT_MTL, IMPORT_EDGES, IMPORT_SMOOTH_ALL, IMPORT_FGON, IMPORT_SMOOTH_GROUPS, IMPORT_MTL_SPLIT, IMPORT_RELATIVE_VERTS)
881                         
882         
883         #orig_scene.makeCurrent() # We can leave them in there new scene.
884         Window.DrawProgressBar(1, '')
885         Window.WaitCursor(0)
886         
887         if count > 1:
888                 print 'Total obj import "%s" dir: %.2f' % (obj_dir, sys.time() - time)
889
890
891 def main():
892         Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ')
893
894 if __name__ == '__main__':
895         main()
896         pass
897
898 #load_obj('/1test.obj')