py api
[blender.git] / release / scripts / import_obj.py
1 #!BPY
2  
3 """
4 Name: 'Wavefront (.obj)...'
5 Blender: 242
6 Group: 'Import'
7 Tooltip: 'Load a Wavefront OBJ File, Shift: batch import all dir.'
8 """
9
10 __author__= "Campbell Barton", "Jiri Hnidek"
11 __url__= ["blender.org", "blenderartists.org"]
12 __version__= "2.0"
13
14 __bpydoc__= """\
15 This script imports a Wavefront OBJ files to Blender.
16
17 Usage:
18 Run this script from "File->Import" menu and then load the desired OBJ file.
19 Note, This loads mesh objects and materials only, nurbs and curves are not supported.
20 """
21
22 # ***** BEGIN GPL LICENSE BLOCK *****
23 #
24 # Script copyright (C) Campbell J Barton 2007
25 #
26 # This program is free software; you can redistribute it and/or
27 # modify it under the terms of the GNU General Public License
28 # as published by the Free Software Foundation; either version 2
29 # of the License, or (at your option) any later version.
30 #
31 # This program is distributed in the hope that it will be useful,
32 # but WITHOUT ANY WARRANTY; without even the implied warranty of
33 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
34 # GNU General Public License for more details.
35 #
36 # You should have received a copy of the GNU General Public License
37 # along with this program; if not, write to the Free Software Foundation,
38 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
39 #
40 # ***** END GPL LICENCE BLOCK *****
41 # --------------------------------------------------------------------------
42
43 from Blender import *
44 import bpy
45 import BPyMesh
46 import BPyImage
47 import BPyMessages
48
49 try:            import os
50 except:         os= False
51
52
53 # Generic path functions
54 def stripFile(path):
55         '''Return directory, where the file is'''
56         lastSlash= max(path.rfind('\\'), path.rfind('/'))
57         if lastSlash != -1:
58                 path= path[:lastSlash]
59         return '%s%s' % (path, sys.sep)
60
61 def stripPath(path):
62         '''Strips the slashes from the back of a string'''
63         return path.split('/')[-1].split('\\')[-1]
64
65 def stripExt(name): # name is a string
66         '''Strips the prefix off the name before writing'''
67         index= name.rfind('.')
68         if index != -1:
69                 return name[ : index ]
70         else:
71                 return name
72 # end path funcs
73
74
75
76 def line_value(line_split):
77         '''
78         Returns 1 string represneting the value for this line
79         None will be returned if theres only 1 word
80         '''
81         length= len(line_split)
82         if length == 1:
83                 return None
84         
85         elif length == 2:
86                 return line_split[1]
87         
88         elif length > 2:
89                 return ' '.join( line_split[1:] )
90
91 def obj_image_load(imagepath, DIR, IMAGE_SEARCH):
92         '''
93         Mainly uses comprehensiveImageLoad
94         but tries to replace '_' with ' ' for Max's exporter replaces spaces with underscores.
95         '''
96         
97         if '_' in imagepath:
98                 image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH)
99                 if image: return image
100                 # Did the exporter rename the image?
101                 image= BPyImage.comprehensiveImageLoad(imagepath.replace('_', ' '), DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH)
102                 if image: return image
103         
104         # Return an image, placeholder if it dosnt exist
105         image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= True, RECURSIVE= IMAGE_SEARCH)
106         return image
107         
108
109 def create_materials(filepath, material_libs, unique_materials, unique_material_images, IMAGE_SEARCH):
110         '''
111         Create all the used materials in this obj,
112         assign colors and images to the materials from all referenced material libs
113         '''
114         DIR= stripFile(filepath)
115         
116         #==================================================================================#
117         # This function sets textures defined in .mtl file                                 #
118         #==================================================================================#
119         def load_material_image(blender_material, context_material_name, imagepath, type):
120                 
121                 texture= bpy.textures.new(type)
122                 texture.setType('Image')
123                 
124                 # Absolute path - c:\.. etc would work here
125                 image= obj_image_load(imagepath, DIR, IMAGE_SEARCH)
126                 has_data = image.has_data
127                 texture.image = image
128                 
129                 # Adds textures for materials (rendering)
130                 if type == 'Kd':
131                         if has_data and image.depth == 32:
132                                 # Image has alpha
133                                 blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL | Texture.MapTo.ALPHA)
134                                 texture.setImageFlags('MipMap', 'InterPol', 'UseAlpha')
135                                 blender_material.mode |= Material.Modes.ZTRANSP
136                                 blender_material.alpha = 0.0
137                         else:
138                                 blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL)
139                                 
140                         # adds textures to faces (Textured/Alt-Z mode)
141                         # Only apply the diffuse texture to the face if the image has not been set with the inline usemat func.
142                         unique_material_images[context_material_name]= image, has_data # set the texface image
143                 
144                 elif type == 'Ka':
145                         blender_material.setTexture(1, texture, Texture.TexCo.UV, Texture.MapTo.CMIR) # TODO- Add AMB to BPY API
146                         
147                 elif type == 'Ks':
148                         blender_material.setTexture(2, texture, Texture.TexCo.UV, Texture.MapTo.SPEC)
149                 
150                 elif type == 'Bump':
151                         blender_material.setTexture(3, texture, Texture.TexCo.UV, Texture.MapTo.NOR)            
152                 elif type == 'D':
153                         blender_material.setTexture(4, texture, Texture.TexCo.UV, Texture.MapTo.ALPHA)                          
154                         blender_material.mode |= Material.Modes.ZTRANSP
155                         blender_material.alpha = 0.0
156                         # Todo, unset deffuse material alpha if it has an alpha channel
157                         
158                 elif type == 'refl':
159                         blender_material.setTexture(5, texture, Texture.TexCo.UV, Texture.MapTo.REF)            
160         
161         
162         # Add an MTL with the same name as the obj if no MTLs are spesified.
163         temp_mtl= stripExt(stripPath(filepath))+ '.mtl'
164         
165         if sys.exists(DIR + temp_mtl) and temp_mtl not in material_libs:
166                         material_libs.append( temp_mtl )
167         del temp_mtl
168         
169         #Create new materials
170         for name in unique_materials.iterkeys():
171                 unique_materials[name]= bpy.materials.new(name)
172                 
173                 unique_material_images[name]= None, False # assign None to all material images to start with, add to later.
174                 
175         unique_materials[None]= None
176         
177         for libname in material_libs:
178                 mtlpath= DIR + libname
179                 if not sys.exists(mtlpath):
180                         #print '\tError Missing MTL: "%s"' % mtlpath
181                         pass
182                 else:
183                         #print '\t\tloading mtl: "%s"' % mtlpath
184                         context_material= None
185                         mtl= open(mtlpath)
186                         for line in mtl.xreadlines():
187                                 if line.startswith('newmtl'):
188                                         context_material_name= line_value(line.split())
189                                         if unique_materials.has_key(context_material_name):
190                                                 context_material = unique_materials[ context_material_name ]
191                                         else:
192                                                 context_material = None
193                                 
194                                 elif context_material:
195                                         # we need to make a material to assign properties to it.
196                                         line_split= line.split()
197                                         line_lower= line.lower()
198                                         if line_lower.startswith('ka'):
199                                                 context_material.setMirCol((float(line_split[1]), float(line_split[2]), float(line_split[3])))
200                                         elif line_lower.startswith('kd'):
201                                                 context_material.setRGBCol((float(line_split[1]), float(line_split[2]), float(line_split[3])))
202                                         elif line_lower.startswith('ks'):
203                                                 context_material.setSpecCol((float(line_split[1]), float(line_split[2]), float(line_split[3])))
204                                         elif line_lower.startswith('ns'):
205                                                 context_material.setHardness( int((float(line_split[1])*0.51)) )
206                                         elif line_lower.startswith('ni'): # Refraction index
207                                                 context_material.setIOR( max(1, min(float(line_split[1]), 3))) # Between 1 and 3
208                                         elif line_lower.startswith('d') or line_lower.startswith('tr'):
209                                                 context_material.setAlpha(float(line_split[1]))
210                                         elif line_lower.startswith('map_ka'):
211                                                 img_filepath= line_value(line.split())
212                                                 load_material_image(context_material, context_material_name, img_filepath, 'Ka')
213                                         elif line_lower.startswith('map_ks'):
214                                                 img_filepath= line_value(line.split())
215                                                 load_material_image(context_material, context_material_name, img_filepath, 'Ks')
216                                         elif line_lower.startswith('map_kd'):
217                                                 img_filepath= line_value(line.split())
218                                                 load_material_image(context_material, context_material_name, img_filepath, 'Kd')
219                                         elif line_lower.startswith('map_bump'):
220                                                 img_filepath= line_value(line.split())
221                                                 load_material_image(context_material, context_material_name, img_filepath, 'Bump')
222                                         elif line_lower.startswith('map_d') or line_lower.startswith('map_tr'): # Alpha map - Dissolve
223                                                 img_filepath= line_value(line.split())
224                                                 load_material_image(context_material, context_material_name, img_filepath, 'D')
225                                         
226                                         elif line_lower.startswith('refl'): # Reflectionmap
227                                                 img_filepath= line_value(line.split())
228                                                 load_material_image(context_material, context_material_name, img_filepath, 'refl')
229                         mtl.close()
230
231
232
233         
234 def split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OBJECTS, SPLIT_MATERIALS):
235         '''
236         Takes vert_loc and faces, and seperates into multiple sets of 
237         (verts_loc, faces, unique_materials, dataname)
238         This is done so objects do not overload the 16 material limit.
239         '''
240         
241         filename = stripExt(stripPath(filepath))
242         
243         if not SPLIT_OBJECTS and not SPLIT_MATERIALS:
244                 # use the filename for the object name since we arnt chopping up the mesh.
245                 return [(verts_loc, faces, unique_materials, filename)]
246         
247         
248         def key_to_name(key):
249                 # if the key is a tuple, join it to make a string
250                 if type(key) == tuple:
251                         return '%s_%s' % key
252                 elif not key:
253                         return filename # assume its a string. make sure this is true if the splitting code is changed
254                 else:
255                         return key
256         
257         # Return a key that makes the faces unique.
258         if SPLIT_OBJECTS and not SPLIT_MATERIALS:
259                 def face_key(face):
260                         return face[4] # object
261         
262         if not SPLIT_OBJECTS and SPLIT_MATERIALS:
263                 def face_key(face):
264                         return face[2] # material
265         
266         else: # Both
267                 def face_key(face):
268                         return face[4], face[2] # object,material               
269         
270         
271         face_split_dict= {}
272         
273         oldkey= -1 # initialize to a value that will never match the key
274         
275         for face in faces:
276                 
277                 key= face_key(face)
278                 
279                 if oldkey != key:
280                         # Check the key has changed.
281                         try:
282                                 verts_split, faces_split, unique_materials_split, vert_remap= face_split_dict[key]
283                         except KeyError:
284                                 faces_split= []
285                                 verts_split= []
286                                 unique_materials_split= {}
287                                 vert_remap= [-1]*len(verts_loc)
288                                 
289                                 face_split_dict[key]= (verts_split, faces_split, unique_materials_split, vert_remap)
290                         
291                         oldkey= key
292                         
293                 face_vert_loc_indicies= face[0]
294                 
295                 # Remap verts to new vert list and add where needed
296                 for enum, i in enumerate(face_vert_loc_indicies):
297                         if vert_remap[i] == -1:
298                                 new_index= len(verts_split)
299                                 vert_remap[i]= new_index # set the new remapped index so we only add once and can reference next time.
300                                 face_vert_loc_indicies[enum] = new_index # remap to the local index
301                                 verts_split.append( verts_loc[i] ) # add the vert to the local verts 
302                                 
303                         else:
304                                 face_vert_loc_indicies[enum] = vert_remap[i] # remap to the local index
305                         
306                         matname= face[2]
307                         if matname and not unique_materials_split.has_key(matname):
308                                 unique_materials_split[matname] = unique_materials[matname]
309                 
310                 faces_split.append(face)
311         
312         
313         # remove one of the itemas and reorder
314         return [(value[0], value[1], value[2], key_to_name(key)) for key, value in face_split_dict.iteritems()]
315
316
317 def create_mesh(new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc, verts_tex, faces, unique_materials, unique_material_images, unique_smooth_groups, dataname):
318         '''
319         Takes all the data gathered and generates a mesh, adding the new object to new_objects
320         deals with fgons, sharp edges and assigning materials
321         '''
322         if not has_ngons:
323                 CREATE_FGONS= False
324         
325         if unique_smooth_groups:
326                 sharp_edges= {}
327                 smooth_group_users= dict([ (context_smooth_group, {}) for context_smooth_group in unique_smooth_groups.iterkeys() ])
328                 context_smooth_group_old= -1
329         
330         # Split fgons into tri's
331         fgon_edges= {} # Used for storing fgon keys
332         if CREATE_EDGES:
333                 edges= []
334         
335         context_object= None
336         
337         # reverse loop through face indicies
338         for f_idx in xrange(len(faces)-1, -1, -1):
339                 
340                 face_vert_loc_indicies,\
341                 face_vert_tex_indicies,\
342                 context_material,\
343                 context_smooth_group,\
344                 context_object= faces[f_idx]
345                 
346                 len_face_vert_loc_indicies = len(face_vert_loc_indicies)
347                 
348                 if len_face_vert_loc_indicies==1:
349                         faces.pop(f_idx)# cant add single vert faces
350                 
351                 elif not face_vert_tex_indicies or len_face_vert_loc_indicies == 2: # faces that have no texture coords are lines
352                         if CREATE_EDGES:
353                                 # generators are better in python 2.4+ but can't be used in 2.3
354                                 # edges.extend( (face_vert_loc_indicies[i], face_vert_loc_indicies[i+1]) for i in xrange(len_face_vert_loc_indicies-1) )
355                                 edges.extend( [(face_vert_loc_indicies[i], face_vert_loc_indicies[i+1]) for i in xrange(len_face_vert_loc_indicies-1)] )
356
357                         faces.pop(f_idx)
358                 else:
359                         
360                         # Smooth Group
361                         if unique_smooth_groups and context_smooth_group:
362                                 # Is a part of of a smooth group and is a face
363                                 if context_smooth_group_old is not context_smooth_group:
364                                         edge_dict= smooth_group_users[context_smooth_group]
365                                         context_smooth_group_old= context_smooth_group
366                                 
367                                 for i in xrange(len_face_vert_loc_indicies):
368                                         i1= face_vert_loc_indicies[i]
369                                         i2= face_vert_loc_indicies[i-1]
370                                         if i1>i2: i1,i2= i2,i1
371                                         
372                                         try:
373                                                 edge_dict[i1,i2]+= 1
374                                         except KeyError:
375                                                 edge_dict[i1,i2]=  1
376                         
377                         # FGons into triangles
378                         if has_ngons and len_face_vert_loc_indicies > 4:
379                                 
380                                 ngon_face_indices= BPyMesh.ngon(verts_loc, face_vert_loc_indicies)
381                                 
382                                 faces.extend(\
383                                 [(\
384                                 [face_vert_loc_indicies[ngon[0]], face_vert_loc_indicies[ngon[1]], face_vert_loc_indicies[ngon[2]] ],\
385                                 [face_vert_tex_indicies[ngon[0]], face_vert_tex_indicies[ngon[1]], face_vert_tex_indicies[ngon[2]] ],\
386                                 context_material,\
387                                 context_smooth_group,\
388                                 context_object)\
389                                 for ngon in ngon_face_indices]\
390                                 )
391                                 
392                                 # edges to make fgons
393                                 if CREATE_FGONS:
394                                         edge_users= {}
395                                         for ngon in ngon_face_indices:
396                                                 for i in (0,1,2):
397                                                         i1= face_vert_loc_indicies[ngon[i  ]]
398                                                         i2= face_vert_loc_indicies[ngon[i-1]]
399                                                         if i1>i2: i1,i2= i2,i1
400                                                         
401                                                         try:
402                                                                 edge_users[i1,i2]+=1
403                                                         except KeyError:
404                                                                 edge_users[i1,i2]= 1
405                                         
406                                         for key, users in edge_users.iteritems():
407                                                 if users>1:
408                                                         fgon_edges[key]= None
409                                 
410                                 # remove all after 3, means we dont have to pop this one.
411                                 faces.pop(f_idx)
412                 
413                 
414         # Build sharp edges
415         if unique_smooth_groups:
416                 for edge_dict in smooth_group_users.itervalues():
417                         for key, users in edge_dict.iteritems():
418                                 if users==1: # This edge is on the boundry of a group
419                                         sharp_edges[key]= None
420         
421         
422         # mat the material names to an index
423         material_mapping= dict([(name, i) for i, name in enumerate(unique_materials.keys())])
424         
425         materials= [None] * len(unique_materials)
426         
427         for name, index in material_mapping.iteritems():
428                 materials[index]= unique_materials[name]
429         
430         me= bpy.meshes.new(dataname)
431         
432         me.materials= materials[0:16] # make sure the list isnt too big.
433         #me.verts.extend([(0,0,0)]) # dummy vert
434         me.verts.extend(verts_loc)
435         
436         face_mapping= me.faces.extend([f[0] for f in faces], indexList=True)
437         
438         if verts_tex and me.faces:
439                 me.faceUV= 1
440                 # TEXMODE= Mesh.FaceModes['TEX']
441         
442         context_material_old= -1 # avoid a dict lookup
443         mat= 0 # rare case it may be un-initialized.
444         me_faces= me.faces
445         ALPHA= Mesh.FaceTranspModes.ALPHA
446         
447         for i, face in enumerate(faces):
448                 if len(face[0]) < 2:
449                         raise "Fooo"
450                 if len(face[0])==2:
451                         if CREATE_EDGES:
452                                 edges.append(face[0])
453                 else:
454                         face_index_map= face_mapping[i]
455                         if face_index_map!=None: # None means the face wasnt added
456                                 blender_face= me_faces[face_index_map]
457                                 
458                                 face_vert_loc_indicies,\
459                                 face_vert_tex_indicies,\
460                                 context_material,\
461                                 context_smooth_group,\
462                                 context_object= face
463                                 
464                                 
465                                 
466                                 if context_smooth_group:
467                                         blender_face.smooth= True
468                                 
469                                 if context_material:
470                                         if context_material_old is not context_material:
471                                                 mat= material_mapping[context_material]
472                                                 if mat>15:
473                                                         mat= 15
474                                                 context_material_old= context_material
475                                         
476                                         blender_face.mat= mat
477                                 
478                                 
479                                 if verts_tex:   
480                                         if context_material:
481                                                 image, has_data= unique_material_images[context_material]
482                                                 if image: # Can be none if the material dosnt have an image.
483                                                         blender_face.image= image
484                                                         if has_data and image.depth == 32:
485                                                                 blender_face.transp |= ALPHA
486                                         
487                                         # BUG - Evil eekadoodle problem where faces that have vert index 0 location at 3 or 4 are shuffled.
488                                         if len(face_vert_loc_indicies)==4:
489                                                 if face_vert_loc_indicies[2]==0 or face_vert_loc_indicies[3]==0:
490                                                         face_vert_tex_indicies= face_vert_tex_indicies[2], face_vert_tex_indicies[3], face_vert_tex_indicies[0], face_vert_tex_indicies[1]
491                                         else: # length of 3
492                                                 if face_vert_loc_indicies[2]==0:
493                                                         face_vert_tex_indicies= face_vert_tex_indicies[1], face_vert_tex_indicies[2], face_vert_tex_indicies[0]
494                                         # END EEEKADOODLE FIX
495                                         
496                                         # assign material, uv's and image
497                                         for ii, uv in enumerate(blender_face.uv):
498                                                 uv.x, uv.y=  verts_tex[face_vert_tex_indicies[ii]]
499         del me_faces
500         del ALPHA
501         
502         # Add edge faces.
503         me_edges= me.edges
504         if CREATE_FGONS and fgon_edges:
505                 FGON= Mesh.EdgeFlags.FGON
506                 for ed in me.findEdges( fgon_edges.keys() ):
507                         if ed!=None:
508                                 me_edges[ed].flag |= FGON
509                 del FGON
510         
511         if unique_smooth_groups and sharp_edges:
512                 SHARP= Mesh.EdgeFlags.SHARP
513                 for ed in me.findEdges( sharp_edges.keys() ):
514                         if ed!=None:
515                                 me_edges[ed].flag |= SHARP
516                 del SHARP
517         
518         if CREATE_EDGES:
519                 me_edges.extend( edges )
520         
521         del me_edges
522         
523         me.calcNormals()
524         
525         scn= Scene.GetCurrent()
526         ob= scn.objects.new(me)
527         new_objects.append(ob)
528
529 def get_float_func(filepath):
530         '''
531         find the float function for this obj file
532         - weather to replace commas or not
533         '''
534         file= open(filepath, 'r')
535         for line in file.xreadlines():
536                 if line.startswith('v'): # vn vt v 
537                         if ',' in line:
538                                 return lambda f: float(f.replace(',', '.'))
539                         elif '.' in line:
540                                 return float
541
542 def load_obj(filepath, CLAMP_SIZE= 0.0, CREATE_FGONS= True, CREATE_SMOOTH_GROUPS= True, CREATE_EDGES= True, SPLIT_OBJECTS= True, SPLIT_GROUPS= True, SPLIT_MATERIALS= True, IMAGE_SEARCH=True):
543         '''
544         Called by the user interface or another script.
545         load_obj(path) - should give acceptable results.
546         This function passes the file and sends the data off
547                 to be split into objects and then converted into mesh objects
548         '''
549         print '\nimporting obj "%s"' % filepath
550         
551         time_main= sys.time()
552         
553         verts_loc= []
554         verts_tex= []
555         faces= [] # tuples of the faces
556         material_libs= [] # filanems to material libs this uses
557         
558         
559         # Get the string to float conversion func for this file- is 'float' for almost all files.
560         float_func= get_float_func(filepath)
561         
562         # Context variables
563         context_material= None
564         context_smooth_group= None
565         context_object= None
566         
567         has_ngons= False
568         # has_smoothgroups= False - is explicit with len(unique_smooth_groups) being > 0
569         
570         # Until we can use sets
571         unique_materials= {}
572         unique_material_images= {}
573         unique_smooth_groups= {}
574         # unique_obects= {} - no use for this variable since the objects are stored in the face.
575         
576         # when there are faces that end with \
577         # it means they are multiline- 
578         # since we use xreadline we cant skip to the next line
579         # so we need to know weather 
580         multi_line_face= False
581         
582         print '\tpassing obj file "%s"...' % filepath,
583         time_sub= sys.time()
584         file= open(filepath, 'r')
585         for line in file.xreadlines():
586                 
587                 if line.startswith('v '):
588                         line_split= line.split()
589                         # rotate X90: (x,-z,y)
590                         verts_loc.append( (float_func(line_split[1]), -float_func(line_split[3]), float_func(line_split[2])) )
591                                 
592                 elif line.startswith('vn '):
593                         pass
594                 
595                 elif line.startswith('vt '):
596                         line_split= line.split()
597                         verts_tex.append( (float_func(line_split[1]), float_func(line_split[2])) ) 
598                 
599                 # Handel faces lines (as faces) and the second+ lines of fa multiline face here
600                 # use 'f' not 'f ' because some objs (very rare have 'fo ' for faces)
601                 elif line.startswith('f') or (line.startswith('l ') and CREATE_EDGES) or multi_line_face:
602                         
603                         if multi_line_face:
604                                 # use face_vert_loc_indicies and face_vert_tex_indicies previously defined and used the obj_face
605                                 line_split= line.split()
606                                 multi_line_face= False
607                                 
608                         else:
609                                 line_split= line[2:].split()
610                                 face_vert_loc_indicies= []
611                                 face_vert_tex_indicies= []
612                                 
613                                 # Instance a face
614                                 faces.append((\
615                                 face_vert_loc_indicies,\
616                                 face_vert_tex_indicies,\
617                                 context_material,\
618                                 context_smooth_group,\
619                                 context_object\
620                                 ))
621                         
622                         if line_split[-1][-1]== '\\':
623                                 multi_line_face= True
624                                 if len(line_split[-1])==1:
625                                         line_split.pop() # remove the \ item
626                                 else:
627                                         line_split[-1]= line_split[-1][:-1] # remove the \ from the end last number
628                         
629                         isline= line.startswith('l')
630                         
631                         for v in line_split:
632                                 obj_vert= v.split('/')
633                                 
634                                 vert_loc_index= int(obj_vert[0])-1
635                                 
636                                 # Make relative negative vert indicies absolute
637                                 if vert_loc_index < 0:
638                                         vert_loc_index= len(verts_loc) + vert_loc_index + 1
639                                 
640                                 face_vert_loc_indicies.append(vert_loc_index)
641                                 
642                                 if not isline:
643                                         if len(obj_vert)>1 and obj_vert[1]:
644                                                 # formatting for faces with normals and textures us 
645                                                 # loc_index/tex_index/nor_index
646                                                 
647                                                 vert_tex_index= int(obj_vert[1])-1
648                                                 # Make relative negative vert indicies absolute
649                                                 if vert_tex_index < 0:
650                                                         vert_tex_index= len(verts_tex) + vert_tex_index + 1
651                                                 
652                                                 face_vert_tex_indicies.append(vert_tex_index)
653                                         else:
654                                                 # dummy
655                                                 face_vert_tex_indicies.append(0)
656                         
657                         if len(face_vert_loc_indicies) > 4:
658                                 has_ngons= True
659                         
660                 elif line.startswith('s'):
661                         if CREATE_SMOOTH_GROUPS:
662                                 context_smooth_group= line_value(line.split())
663                                 if context_smooth_group=='off':
664                                         context_smooth_group= None
665                                 elif context_smooth_group: # is not None
666                                         unique_smooth_groups[context_smooth_group]= None
667                 
668                 elif line.startswith('o'):
669                         if SPLIT_OBJECTS:
670                                 context_object= line_value(line.split())
671                                 # unique_obects[context_object]= None
672                         
673                 elif line.startswith('g'):
674                         if SPLIT_GROUPS:
675                                 context_object= line_value(line.split())
676                                 # print 'context_object', context_object
677                                 # unique_obects[context_object]= None
678                 
679                 elif line.startswith('usemtl'):
680                         context_material= line_value(line.split())
681                         unique_materials[context_material]= None
682                 elif line.startswith('mtllib'): # usemap or usemat
683                         material_libs.extend( line.split()[1:] ) # can have multiple mtllib filenames per line
684                 
685                 ''' # How to use usemap? depricated?
686                 elif line.startswith('usema'): # usemap or usemat
687                         context_image= line_value(line.split())
688                 '''
689         
690         file.close()
691         time_new= sys.time()
692         print '%.4f sec' % (time_new-time_sub)
693         time_sub= time_new
694         
695         
696         print '\tloading materials and images...',
697         create_materials(filepath, material_libs, unique_materials, unique_material_images, IMAGE_SEARCH)
698         
699         time_new= sys.time()
700         print '%.4f sec' % (time_new-time_sub)
701         time_sub= time_new
702         
703         
704         # deselect all
705         Scene.GetCurrent().objects.selected = []
706         new_objects= [] # put new objects here
707         
708         print '\tbuilding geometry...\n\tverts:%i faces:%i materials: %i smoothgroups:%i ...' % ( len(verts_loc), len(faces), len(unique_materials), len(unique_smooth_groups) ),
709         # Split the mesh by objects/materials, may 
710         for verts_loc_split, faces_split, unique_materials_split, dataname in split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OBJECTS, SPLIT_MATERIALS):
711                 # Create meshes from the data
712                 create_mesh(new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc_split, verts_tex, faces_split, unique_materials_split, unique_material_images, unique_smooth_groups, dataname)
713         
714         axis_min= [ 1000000000]*3
715         axis_max= [-1000000000]*3
716         
717         if CLAMP_SIZE:
718                 # Get all object bounds
719                 for ob in new_objects:
720                         for v in ob.getBoundBox():
721                                 for axis, value in enumerate(v):
722                                         if axis_min[axis] > value:      axis_min[axis]= value
723                                         if axis_max[axis] < value:      axis_max[axis]= value
724                 
725                 # Scale objects
726                 max_axis= max(axis_max[0]-axis_min[0], axis_max[1]-axis_min[1], axis_max[2]-axis_min[2])
727                 scale= 1.0
728                 
729                 while CLAMP_SIZE < max_axis * scale:
730                         scale= scale/10.0
731                 
732                 for ob in new_objects:
733                         ob.setSize(scale, scale, scale)
734         
735         time_new= sys.time()
736         
737         print '%.4f sec' % (time_new-time_sub)
738         print 'finished importing: "%s" in %.4f sec.' % (filepath, (time_new-time_main))
739
740
741 DEBUG= True
742
743
744 def load_obj_ui(filepath, BATCH_LOAD= False):
745         if BPyMessages.Error_NoFile(filepath):
746                 return
747         
748         
749         CREATE_SMOOTH_GROUPS= Draw.Create(0)
750         CREATE_FGONS= Draw.Create(1)
751         CREATE_EDGES= Draw.Create(1)
752         SPLIT_OBJECTS= Draw.Create(1)
753         SPLIT_GROUPS= Draw.Create(1)
754         SPLIT_MATERIALS= Draw.Create(1)
755         MORPH_TARGET= Draw.Create(0)
756         CLAMP_SIZE= Draw.Create(10.0)
757         IMAGE_SEARCH= Draw.Create(1)
758         
759         
760         # Get USER Options
761         pup_block= [\
762         'Import...',\
763         ('Smooth Groups', CREATE_SMOOTH_GROUPS, 'Surround smooth groups by sharp edges'),\
764         ('Create FGons', CREATE_FGONS, 'Import faces with more then 4 verts as fgons.'),\
765         ('Lines', CREATE_EDGES, 'Import lines and faces with 2 verts as edges'),\
766         'Seperate objects from obj...',\
767         ('Object', SPLIT_OBJECTS, 'Import OBJ Objects into Blender Objects'),\
768         ('Group', SPLIT_GROUPS, 'Import OBJ Groups into Blender Objects'),\
769         ('Material', SPLIT_MATERIALS, 'Import each material into a seperate mesh (Avoids > 16 per mesh error)'),\
770         'Options...',\
771         ('Morph Target', MORPH_TARGET, 'Keep vert and face order, disables some other options.'),\
772         ('Clamp Scale:', CLAMP_SIZE, 0.0, 1000.0, 'Clamp the size to this maximum (Zero to Disable)'),\
773         ('Image Search', IMAGE_SEARCH, 'Search subdirs for any assosiated images (Warning, may be slow)'),\
774         ]
775         
776         if not Draw.PupBlock('Import OBJ...', pup_block):
777                 return
778         
779         if MORPH_TARGET.val:
780                 SPLIT_OBJECTS.val = False
781                 SPLIT_GROUPS.val = False
782                 SPLIT_MATERIALS.val = False
783         
784         Window.WaitCursor(1)
785         
786         if BATCH_LOAD: # load the dir
787                 try:
788                         files= [ f for f in os.listdir(filepath) if f.lower().endswith('.obj') ]
789                 except:
790                         Window.WaitCursor(0)
791                         Draw.PupMenu('Error%t|Could not open path ' + filepath)
792                         return
793                 
794                 if not files:
795                         Window.WaitCursor(0)
796                         Draw.PupMenu('Error%t|No files at path ' + filepath)
797                         return
798                 
799                 for f in files:
800                         scn= bpy.scenes.new( stripExt(f) )
801                         scn.makeCurrent()
802                         
803                         load_obj(sys.join(filepath, f),\
804                           CLAMP_SIZE.val,\
805                           CREATE_FGONS.val,\
806                           CREATE_SMOOTH_GROUPS.val,\
807                           CREATE_EDGES.val,\
808                           SPLIT_OBJECTS.val,\
809                           SPLIT_GROUPS.val,\
810                           SPLIT_MATERIALS.val,\
811                           IMAGE_SEARCH.val,\
812                         )
813         
814         else: # Normal load
815                 load_obj(filepath,\
816                   CLAMP_SIZE.val,\
817                   CREATE_FGONS.val,\
818                   CREATE_SMOOTH_GROUPS.val,\
819                   CREATE_EDGES.val,\
820                   SPLIT_OBJECTS.val,\
821                   SPLIT_GROUPS.val,\
822                   SPLIT_MATERIALS.val,\
823                   IMAGE_SEARCH.val,\
824                 )
825         
826         Window.WaitCursor(0)
827
828
829 def load_obj_ui_batch(file):
830         load_obj_ui(file, True)
831
832 DEBUG= False
833
834 if __name__=='__main__' and not DEBUG:
835         if os and Window.GetKeyQualifiers() & Window.Qual.SHIFT:
836                 Window.FileSelector(load_obj_ui_batch, 'Import OBJ Dir', '')
837         else:
838                 Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ', '*.obj')
839
840
841 '''
842 # For testing compatibility
843 else:
844         # DEBUG ONLY
845         TIME= sys.time()
846         import os
847         print 'Searching for files'
848         os.system('find /fe/obj -iname "*.obj" > /tmp/temp3ds_list')
849         
850         print '...Done'
851         file= open('/tmp/temp3ds_list', 'r')
852         lines= file.readlines()
853         file.close()
854
855         def between(v,a,b):
856                 if v <= max(a,b) and v >= min(a,b):
857                         return True             
858                 return False
859                 
860         for i, _obj in enumerate(lines):
861                 if between(i, 0,20):
862                         _obj= _obj[:-1]
863                         print 'Importing', _obj, '\nNUMBER', i, 'of', len(lines)
864                         _obj_file= _obj.split('/')[-1].split('\\')[-1]
865                         newScn= bpy.scenes.new(_obj_file)
866                         newScn.makeCurrent()
867                         load_obj(_obj, False)
868
869         print 'TOTAL TIME: %.6f' % (sys.time() - TIME)
870 '''
871 #load_obj('/test.obj')
872 #load_obj('/fe/obj/mba1.obj')