4 Name: 'Wavefront (.obj)...'
7 Tooltip: 'Load a Wavefront OBJ File, Shift: batch import all dir.'
10 __author__= "Campbell Barton", "Jiri Hnidek", "Paolo Ciccone"
11 __url__= ['http://wiki.blender.org/index.php/Scripts/Manual/Import/wavefront_obj', 'blender.org', 'blenderartists.org']
15 This script imports a Wavefront OBJ files to Blender.
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.
22 # ***** BEGIN GPL LICENSE BLOCK *****
24 # Script copyright (C) Campbell J Barton 2007
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.
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.
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.
40 # ***** END GPL LICENCE BLOCK *****
41 # --------------------------------------------------------------------------
43 from Blender import Mesh, Draw, Window, Texture, Material, sys
52 # Generic path functions
54 '''Return directory, where the file is'''
55 lastSlash= max(path.rfind('\\'), path.rfind('/'))
57 path= path[:lastSlash]
58 return '%s%s' % (path, sys.sep)
61 '''Strips the slashes from the back of a string'''
62 return path.split('/')[-1].split('\\')[-1]
64 def stripExt(name): # name is a string
65 '''Strips the prefix off the name before writing'''
66 index= name.rfind('.')
68 return name[ : index ]
75 def line_value(line_split):
77 Returns 1 string represneting the value for this line
78 None will be returned if theres only 1 word
80 length= len(line_split)
88 return ' '.join( line_split[1:] )
90 def obj_image_load(imagepath, DIR, IMAGE_SEARCH):
92 Mainly uses comprehensiveImageLoad
93 but tries to replace '_' with ' ' for Max's exporter replaces spaces with underscores.
97 image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH)
98 if image: return image
99 # Did the exporter rename the image?
100 image= BPyImage.comprehensiveImageLoad(imagepath.replace('_', ' '), DIR, PLACE_HOLDER= False, RECURSIVE= IMAGE_SEARCH)
101 if image: return image
103 # Return an image, placeholder if it dosnt exist
104 image= BPyImage.comprehensiveImageLoad(imagepath, DIR, PLACE_HOLDER= True, RECURSIVE= IMAGE_SEARCH)
108 def create_materials(filepath, material_libs, unique_materials, unique_material_images, IMAGE_SEARCH):
110 Create all the used materials in this obj,
111 assign colors and images to the materials from all referenced material libs
113 DIR= stripFile(filepath)
115 #==================================================================================#
116 # This function sets textures defined in .mtl file #
117 #==================================================================================#
118 def load_material_image(blender_material, context_material_name, imagepath, type):
120 texture= bpy.data.textures.new(type)
121 texture.setType('Image')
123 # Absolute path - c:\.. etc would work here
124 image= obj_image_load(imagepath, DIR, IMAGE_SEARCH)
125 has_data = image.has_data
126 texture.image = image
128 # Adds textures for materials (rendering)
130 if has_data and image.depth == 32:
132 blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL | Texture.MapTo.ALPHA)
133 texture.setImageFlags('MipMap', 'InterPol', 'UseAlpha')
134 blender_material.mode |= Material.Modes.ZTRANSP
135 blender_material.alpha = 0.0
137 blender_material.setTexture(0, texture, Texture.TexCo.UV, Texture.MapTo.COL)
139 # adds textures to faces (Textured/Alt-Z mode)
140 # Only apply the diffuse texture to the face if the image has not been set with the inline usemat func.
141 unique_material_images[context_material_name]= image, has_data # set the texface image
144 blender_material.setTexture(1, texture, Texture.TexCo.UV, Texture.MapTo.CMIR) # TODO- Add AMB to BPY API
147 blender_material.setTexture(2, texture, Texture.TexCo.UV, Texture.MapTo.SPEC)
150 blender_material.setTexture(3, texture, Texture.TexCo.UV, Texture.MapTo.NOR)
152 blender_material.setTexture(4, texture, Texture.TexCo.UV, Texture.MapTo.ALPHA)
153 blender_material.mode |= Material.Modes.ZTRANSP
154 blender_material.alpha = 0.0
155 # Todo, unset deffuse material alpha if it has an alpha channel
158 blender_material.setTexture(5, texture, Texture.TexCo.UV, Texture.MapTo.REF)
161 # Add an MTL with the same name as the obj if no MTLs are spesified.
162 temp_mtl= stripExt(stripPath(filepath))+ '.mtl'
164 if sys.exists(DIR + temp_mtl) and temp_mtl not in material_libs:
165 material_libs.append( temp_mtl )
168 #Create new materials
169 for name in unique_materials: # .keys()
171 unique_materials[name]= bpy.data.materials.new(name)
172 unique_material_images[name]= None, False # assign None to all material images to start with, add to later.
174 unique_materials[None]= None
175 unique_material_images[None]= None, False
177 for libname in material_libs:
178 mtlpath= DIR + libname
179 if not sys.exists(mtlpath):
180 #print '\tError Missing MTL: "%s"' % mtlpath
183 #print '\t\tloading mtl: "%s"' % mtlpath
184 context_material= None
185 mtl= open(mtlpath, 'rU')
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 ]
192 context_material = None
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().lstrip()
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())
213 load_material_image(context_material, context_material_name, img_filepath, 'Ka')
214 elif line_lower.startswith('map_ks'):
215 img_filepath= line_value(line.split())
217 load_material_image(context_material, context_material_name, img_filepath, 'Ks')
218 elif line_lower.startswith('map_kd'):
219 img_filepath= line_value(line.split())
221 load_material_image(context_material, context_material_name, img_filepath, 'Kd')
222 elif line_lower.startswith('map_bump'):
223 img_filepath= line_value(line.split())
225 load_material_image(context_material, context_material_name, img_filepath, 'Bump')
226 elif line_lower.startswith('map_d') or line_lower.startswith('map_tr'): # Alpha map - Dissolve
227 img_filepath= line_value(line.split())
229 load_material_image(context_material, context_material_name, img_filepath, 'D')
231 elif line_lower.startswith('refl'): # Reflectionmap
232 img_filepath= line_value(line.split())
234 load_material_image(context_material, context_material_name, img_filepath, 'refl')
240 def split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP, SPLIT_MATERIALS):
242 Takes vert_loc and faces, and seperates into multiple sets of
243 (verts_loc, faces, unique_materials, dataname)
244 This is done so objects do not overload the 16 material limit.
247 filename = stripExt(stripPath(filepath))
249 if not SPLIT_OB_OR_GROUP and not SPLIT_MATERIALS:
250 # use the filename for the object name since we arnt chopping up the mesh.
251 return [(verts_loc, faces, unique_materials, filename)]
254 def key_to_name(key):
255 # if the key is a tuple, join it to make a string
256 if type(key) == tuple:
259 return filename # assume its a string. make sure this is true if the splitting code is changed
263 # Return a key that makes the faces unique.
264 if SPLIT_OB_OR_GROUP and not SPLIT_MATERIALS:
266 return face[4] # object
268 elif not SPLIT_OB_OR_GROUP and SPLIT_MATERIALS:
270 return face[2] # material
274 return face[4], face[2] # object,material
279 oldkey= -1 # initialize to a value that will never match the key
286 # Check the key has changed.
288 verts_split, faces_split, unique_materials_split, vert_remap= face_split_dict[key]
292 unique_materials_split= {}
293 vert_remap= [-1]*len(verts_loc)
295 face_split_dict[key]= (verts_split, faces_split, unique_materials_split, vert_remap)
299 face_vert_loc_indicies= face[0]
301 # Remap verts to new vert list and add where needed
302 for enum, i in enumerate(face_vert_loc_indicies):
303 if vert_remap[i] == -1:
304 new_index= len(verts_split)
305 vert_remap[i]= new_index # set the new remapped index so we only add once and can reference next time.
306 face_vert_loc_indicies[enum] = new_index # remap to the local index
307 verts_split.append( verts_loc[i] ) # add the vert to the local verts
310 face_vert_loc_indicies[enum] = vert_remap[i] # remap to the local index
313 if matname and not unique_materials_split.has_key(matname):
314 unique_materials_split[matname] = unique_materials[matname]
316 faces_split.append(face)
319 # remove one of the itemas and reorder
320 return [(value[0], value[1], value[2], key_to_name(key)) for key, value in face_split_dict.iteritems()]
323 def create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc, verts_tex, faces, unique_materials, unique_material_images, unique_smooth_groups, vertex_groups, dataname):
325 Takes all the data gathered and generates a mesh, adding the new object to new_objects
326 deals with fgons, sharp edges and assigning materials
331 if unique_smooth_groups:
333 smooth_group_users= dict([ (context_smooth_group, {}) for context_smooth_group in unique_smooth_groups.iterkeys() ])
334 context_smooth_group_old= -1
336 # Split fgons into tri's
337 fgon_edges= {} # Used for storing fgon keys
343 # reverse loop through face indicies
344 for f_idx in xrange(len(faces)-1, -1, -1):
346 face_vert_loc_indicies,\
347 face_vert_tex_indicies,\
349 context_smooth_group,\
350 context_object= faces[f_idx]
352 len_face_vert_loc_indicies = len(face_vert_loc_indicies)
354 if len_face_vert_loc_indicies==1:
355 faces.pop(f_idx)# cant add single vert faces
357 elif not face_vert_tex_indicies or len_face_vert_loc_indicies == 2: # faces that have no texture coords are lines
359 # generators are better in python 2.4+ but can't be used in 2.3
360 # edges.extend( (face_vert_loc_indicies[i], face_vert_loc_indicies[i+1]) for i in xrange(len_face_vert_loc_indicies-1) )
361 edges.extend( [(face_vert_loc_indicies[i], face_vert_loc_indicies[i+1]) for i in xrange(len_face_vert_loc_indicies-1)] )
367 if unique_smooth_groups and context_smooth_group:
368 # Is a part of of a smooth group and is a face
369 if context_smooth_group_old is not context_smooth_group:
370 edge_dict= smooth_group_users[context_smooth_group]
371 context_smooth_group_old= context_smooth_group
373 for i in xrange(len_face_vert_loc_indicies):
374 i1= face_vert_loc_indicies[i]
375 i2= face_vert_loc_indicies[i-1]
376 if i1>i2: i1,i2= i2,i1
383 # FGons into triangles
384 if has_ngons and len_face_vert_loc_indicies > 4:
386 ngon_face_indices= BPyMesh.ngon(verts_loc, face_vert_loc_indicies)
389 [face_vert_loc_indicies[ngon[0]], face_vert_loc_indicies[ngon[1]], face_vert_loc_indicies[ngon[2]] ],\
390 [face_vert_tex_indicies[ngon[0]], face_vert_tex_indicies[ngon[1]], face_vert_tex_indicies[ngon[2]] ],\
392 context_smooth_group,\
394 for ngon in ngon_face_indices]\
397 # edges to make fgons
400 for ngon in ngon_face_indices:
402 i1= face_vert_loc_indicies[ngon[i ]]
403 i2= face_vert_loc_indicies[ngon[i-1]]
404 if i1>i2: i1,i2= i2,i1
411 for key, users in edge_users.iteritems():
413 fgon_edges[key]= None
415 # remove all after 3, means we dont have to pop this one.
420 if unique_smooth_groups:
421 for edge_dict in smooth_group_users.itervalues():
422 for key, users in edge_dict.iteritems():
423 if users==1: # This edge is on the boundry of a group
424 sharp_edges[key]= None
427 # map the material names to an index
428 material_mapping= dict([(name, i) for i, name in enumerate(unique_materials)]) # enumerate over unique_materials keys()
430 materials= [None] * len(unique_materials)
432 for name, index in material_mapping.iteritems():
433 materials[index]= unique_materials[name]
435 me= bpy.data.meshes.new(dataname)
437 me.materials= materials[0:16] # make sure the list isnt too big.
438 #me.verts.extend([(0,0,0)]) # dummy vert
439 me.verts.extend(verts_loc)
441 face_mapping= me.faces.extend([f[0] for f in faces], indexList=True)
443 if verts_tex and me.faces:
445 # TEXMODE= Mesh.FaceModes['TEX']
447 context_material_old= -1 # avoid a dict lookup
448 mat= 0 # rare case it may be un-initialized.
450 ALPHA= Mesh.FaceTranspModes.ALPHA
452 for i, face in enumerate(faces):
454 pass #raise "bad face"
455 elif len(face[0])==2:
457 edges.append(face[0])
459 face_index_map= face_mapping[i]
460 if face_index_map!=None: # None means the face wasnt added
461 blender_face= me_faces[face_index_map]
463 face_vert_loc_indicies,\
464 face_vert_tex_indicies,\
466 context_smooth_group,\
471 if context_smooth_group:
472 blender_face.smooth= True
475 if context_material_old is not context_material:
476 mat= material_mapping[context_material]
479 context_material_old= context_material
481 blender_face.mat= mat
486 image, has_data= unique_material_images[context_material]
487 if image: # Can be none if the material dosnt have an image.
488 blender_face.image= image
489 if has_data and image.depth == 32:
490 blender_face.transp |= ALPHA
492 # BUG - Evil eekadoodle problem where faces that have vert index 0 location at 3 or 4 are shuffled.
493 if len(face_vert_loc_indicies)==4:
494 if face_vert_loc_indicies[2]==0 or face_vert_loc_indicies[3]==0:
495 face_vert_tex_indicies= face_vert_tex_indicies[2], face_vert_tex_indicies[3], face_vert_tex_indicies[0], face_vert_tex_indicies[1]
497 if face_vert_loc_indicies[2]==0:
498 face_vert_tex_indicies= face_vert_tex_indicies[1], face_vert_tex_indicies[2], face_vert_tex_indicies[0]
499 # END EEEKADOODLE FIX
501 # assign material, uv's and image
502 for ii, uv in enumerate(blender_face.uv):
503 uv.x, uv.y= verts_tex[face_vert_tex_indicies[ii]]
509 if CREATE_FGONS and fgon_edges:
510 FGON= Mesh.EdgeFlags.FGON
511 for ed in me.findEdges( fgon_edges.keys() ):
513 me_edges[ed].flag |= FGON
516 if unique_smooth_groups and sharp_edges:
517 SHARP= Mesh.EdgeFlags.SHARP
518 for ed in me.findEdges( sharp_edges.keys() ):
520 me_edges[ed].flag |= SHARP
524 me_edges.extend( edges )
530 ob= scn.objects.new(me)
531 new_objects.append(ob)
533 # Create the vertex groups. No need to have the flag passed here since we test for the
534 # content of the vertex_groups. If the user selects to NOT have vertex groups saved then
535 # the following test will never run
536 for group_name, group_indicies in vertex_groups.iteritems():
537 me.addVertGroup(group_name)
538 me.assignVertsToGroup(group_name, group_indicies,1.00, Mesh.AssignModes.REPLACE)
541 def create_nurbs(scn, context_nurbs, vert_loc, new_objects):
543 Add nurbs object to blender, only support one type at the moment
545 deg = context_nurbs.get('deg', (3,))
546 curv_range = context_nurbs.get('curv_range', None)
547 curv_idx = context_nurbs.get('curv_idx', [])
548 parm_u = context_nurbs.get('parm_u', [])
549 parm_v = context_nurbs.get('parm_v', [])
550 name = context_nurbs.get('name', 'ObjNurb')
551 cstype = context_nurbs.get('cstype', None)
554 print '\tWarning, cstype not found'
556 if cstype != 'bspline':
557 print '\tWarning, cstype is not supported (only bspline)'
560 print '\tWarning, curv argument empty or not set'
562 if len(deg) > 1 or parm_v:
563 print '\tWarning, surfaces not supported'
566 cu = bpy.data.curves.new(name, 'Curve')
567 cu.flag |= 1 # 3D curve
573 pt = (pt[0], pt[1], pt[2], 1.0)
576 nu = cu.appendNurb(pt)
582 # get for endpoint flag from the weighting
583 if curv_range and len(parm_u) > deg[0]+1:
585 for i in xrange(deg[0]+1):
587 if abs(parm_u[i]-curv_range[0]) > 0.0001:
591 if abs(parm_u[-(i+1)]-curv_range[1]) > 0.0001:
605 if len(parm_u) > deg[0]+1:
606 for i in xrange(deg[0]+1):
607 #print curv_idx[i], curv_idx[-(i+1)]
609 if curv_idx[i]==curv_idx[-(i+1)]:
617 ob = scn.objects.new(cu)
618 new_objects.append(ob)
621 def strip_slash(line_split):
622 if line_split[-1][-1]== '\\':
623 if len(line_split[-1])==1:
624 line_split.pop() # remove the \ item
626 line_split[-1]= line_split[-1][:-1] # remove the \ from the end last number
632 def get_float_func(filepath):
634 find the float function for this obj file
635 - weather to replace commas or not
637 file= open(filepath, 'rU')
638 for line in file: #.xreadlines():
640 if line.startswith('v'): # vn vt v
642 return lambda f: float(f.replace(',', '.'))
646 # incase all vert values were ints
649 def load_obj(filepath,
652 CREATE_SMOOTH_GROUPS= True,
656 SPLIT_MATERIALS= True,
661 Called by the user interface or another script.
662 load_obj(path) - should give acceptable results.
663 This function passes the file and sends the data off
664 to be split into objects and then converted into mesh objects
666 print '\nimporting obj "%s"' % filepath
668 if SPLIT_OBJECTS or SPLIT_GROUPS or SPLIT_MATERIALS:
671 time_main= sys.time()
675 faces= [] # tuples of the faces
676 material_libs= [] # filanems to material libs this uses
677 vertex_groups = {} # when POLYGROUPS is true
679 # Get the string to float conversion func for this file- is 'float' for almost all files.
680 float_func= get_float_func(filepath)
683 context_material= None
684 context_smooth_group= None
686 context_vgroup = None
691 context_parm = '' # used by nurbs too but could be used elsewhere
694 # has_smoothgroups= False - is explicit with len(unique_smooth_groups) being > 0
696 # Until we can use sets
698 unique_material_images= {}
699 unique_smooth_groups= {}
700 # unique_obects= {} - no use for this variable since the objects are stored in the face.
702 # when there are faces that end with \
703 # it means they are multiline-
704 # since we use xreadline we cant skip to the next line
705 # so we need to know weather
706 context_multi_line= ''
708 print '\tparsing obj file "%s"...' % filepath,
711 file= open(filepath, 'rU')
712 for line in file: #.xreadlines():
713 line = line.lstrip() # rare cases there is white space at the start of the line
715 if line.startswith('v '):
716 line_split= line.split()
717 # rotate X90: (x,-z,y)
718 verts_loc.append( (float_func(line_split[1]), -float_func(line_split[3]), float_func(line_split[2])) )
720 elif line.startswith('vn '):
723 elif line.startswith('vt '):
724 line_split= line.split()
725 verts_tex.append( (float_func(line_split[1]), float_func(line_split[2])) )
727 # Handel faces lines (as faces) and the second+ lines of fa multiline face here
728 # use 'f' not 'f ' because some objs (very rare have 'fo ' for faces)
729 elif line.startswith('f') or context_multi_line == 'f':
731 if context_multi_line:
732 # use face_vert_loc_indicies and face_vert_tex_indicies previously defined and used the obj_face
733 line_split= line.split()
736 line_split= line[2:].split()
737 face_vert_loc_indicies= []
738 face_vert_tex_indicies= []
742 face_vert_loc_indicies,\
743 face_vert_tex_indicies,\
745 context_smooth_group,\
749 if strip_slash(line_split):
750 context_multi_line = 'f'
752 context_multi_line = ''
755 obj_vert= v.split('/')
757 vert_loc_index= int(obj_vert[0])-1
758 # Add the vertex to the current group
759 # *warning*, this wont work for files that have groups defined around verts
760 if POLYGROUPS and context_vgroup:
761 vertex_groups[context_vgroup].append(vert_loc_index)
763 # Make relative negative vert indicies absolute
764 if vert_loc_index < 0:
765 vert_loc_index= len(verts_loc) + vert_loc_index + 1
767 face_vert_loc_indicies.append(vert_loc_index)
769 if len(obj_vert)>1 and obj_vert[1]:
770 # formatting for faces with normals and textures us
771 # loc_index/tex_index/nor_index
773 vert_tex_index= int(obj_vert[1])-1
774 # Make relative negative vert indicies absolute
775 if vert_tex_index < 0:
776 vert_tex_index= len(verts_tex) + vert_tex_index + 1
778 face_vert_tex_indicies.append(vert_tex_index)
781 face_vert_tex_indicies.append(0)
783 if len(face_vert_loc_indicies) > 4:
786 elif CREATE_EDGES and (line.startswith('l ') or context_multi_line == 'l'):
787 # very similar to the face load function above with some parts removed
789 if context_multi_line:
790 # use face_vert_loc_indicies and face_vert_tex_indicies previously defined and used the obj_face
791 line_split= line.split()
794 line_split= line[2:].split()
795 face_vert_loc_indicies= []
796 face_vert_tex_indicies= []
800 face_vert_loc_indicies,\
801 face_vert_tex_indicies,\
803 context_smooth_group,\
807 if strip_slash(line_split):
808 context_multi_line = 'l'
810 context_multi_line = ''
812 isline= line.startswith('l')
815 vert_loc_index= int(v)-1
817 # Make relative negative vert indicies absolute
818 if vert_loc_index < 0:
819 vert_loc_index= len(verts_loc) + vert_loc_index + 1
821 face_vert_loc_indicies.append(vert_loc_index)
823 elif line.startswith('s'):
824 if CREATE_SMOOTH_GROUPS:
825 context_smooth_group= line_value(line.split())
826 if context_smooth_group=='off':
827 context_smooth_group= None
828 elif context_smooth_group: # is not None
829 unique_smooth_groups[context_smooth_group]= None
831 elif line.startswith('o'):
833 context_object= line_value(line.split())
834 # unique_obects[context_object]= None
836 elif line.startswith('g'):
838 context_object= line_value(line.split())
839 # print 'context_object', context_object
840 # unique_obects[context_object]= None
842 context_vgroup = line_value(line.split())
843 if context_vgroup and context_vgroup != '(null)':
844 vertex_groups.setdefault(context_vgroup, [])
846 context_vgroup = None # dont assign a vgroup
848 elif line.startswith('usemtl'):
849 context_material= line_value(line.split())
850 unique_materials[context_material]= None
851 elif line.startswith('mtllib'): # usemap or usemat
852 material_libs.extend( line.split()[1:] ) # can have multiple mtllib filenames per line
856 elif line.startswith('cstype '):
857 context_nurbs['cstype']= line_value(line.split()) # 'rat bspline' / 'bspline'
858 elif line.startswith('curv ') or context_multi_line == 'curv':
859 line_split= line.split()
861 curv_idx = context_nurbs['curv_idx'] = context_nurbs.get('curv_idx', []) # incase were multiline
863 if not context_multi_line:
864 context_nurbs['curv_range'] = float_func(line_split[1]), float_func(line_split[2])
865 line_split[0:3] = [] # remove first 3 items
867 if strip_slash(line_split):
868 context_multi_line = 'curv'
870 context_multi_line = ''
874 vert_loc_index = int(i)-1
876 if vert_loc_index < 0:
877 vert_loc_index= len(verts_loc) + vert_loc_index + 1
879 curv_idx.append(vert_loc_index)
881 elif line.startswith('parm') or context_multi_line == 'parm':
882 line_split= line.split()
884 if context_multi_line:
885 context_multi_line = ''
887 context_parm = line_split[1]
888 line_split[0:2] = [] # remove first 2
890 if strip_slash(line_split):
891 context_multi_line = 'parm'
893 context_multi_line = ''
895 if context_parm.lower() == 'u':
896 context_nurbs.setdefault('parm_u', []).extend( [float_func(f) for f in line_split] )
897 elif context_parm.lower() == 'v': # surfaces not suported yet
898 context_nurbs.setdefault('parm_v', []).extend( [float_func(f) for f in line_split] )
899 # else: # may want to support other parm's ?
901 elif line.startswith('deg '):
902 context_nurbs['deg']= [int(i) for i in line.split()[1:]]
903 elif line.startswith('end'):
904 # Add the nurbs curve
906 context_nurbs['name'] = context_object
907 nurbs.append(context_nurbs)
911 ''' # How to use usemap? depricated?
912 elif line.startswith('usema'): # usemap or usemat
913 context_image= line_value(line.split())
918 print '%.4f sec' % (time_new-time_sub)
922 print '\tloading materials and images...',
923 create_materials(filepath, material_libs, unique_materials, unique_material_images, IMAGE_SEARCH)
926 print '%.4f sec' % (time_new-time_sub)
930 verts_loc[:] = [(v[0], v[2], -v[1]) for v in verts_loc]
933 scn = bpy.data.scenes.active
934 scn.objects.selected = []
935 new_objects= [] # put new objects here
937 print '\tbuilding geometry...\n\tverts:%i faces:%i materials: %i smoothgroups:%i ...' % ( len(verts_loc), len(faces), len(unique_materials), len(unique_smooth_groups) ),
938 # Split the mesh by objects/materials, may
939 if SPLIT_OBJECTS or SPLIT_GROUPS: SPLIT_OB_OR_GROUP = True
940 else: SPLIT_OB_OR_GROUP = False
942 for verts_loc_split, faces_split, unique_materials_split, dataname in split_mesh(verts_loc, faces, unique_materials, filepath, SPLIT_OB_OR_GROUP, SPLIT_MATERIALS):
943 # Create meshes from the data, warning 'vertex_groups' wont support splitting
944 create_mesh(scn, new_objects, has_ngons, CREATE_FGONS, CREATE_EDGES, verts_loc_split, verts_tex, faces_split, unique_materials_split, unique_material_images, unique_smooth_groups, vertex_groups, dataname)
947 for context_nurbs in nurbs:
948 create_nurbs(scn, context_nurbs, verts_loc, new_objects)
951 axis_min= [ 1000000000]*3
952 axis_max= [-1000000000]*3
955 # Get all object bounds
956 for ob in new_objects:
957 for v in ob.getBoundBox():
958 for axis, value in enumerate(v):
959 if axis_min[axis] > value: axis_min[axis]= value
960 if axis_max[axis] < value: axis_max[axis]= value
963 max_axis= max(axis_max[0]-axis_min[0], axis_max[1]-axis_min[1], axis_max[2]-axis_min[2])
966 while CLAMP_SIZE < max_axis * scale:
969 for ob in new_objects:
970 ob.setSize(scale, scale, scale)
972 # Better rotate the vert locations
974 # for ob in new_objects:
975 # ob.RotX = -1.570796326794896558
979 print '%.4f sec' % (time_new-time_sub)
980 print 'finished importing: "%s" in %.4f sec.' % (filepath, (time_new-time_main))
986 def load_obj_ui(filepath, BATCH_LOAD= False):
987 if BPyMessages.Error_NoFile(filepath):
990 global CREATE_SMOOTH_GROUPS, CREATE_FGONS, CREATE_EDGES, SPLIT_OBJECTS, SPLIT_GROUPS, SPLIT_MATERIALS, CLAMP_SIZE, IMAGE_SEARCH, POLYGROUPS, KEEP_VERT_ORDER, ROTATE_X90
992 CREATE_SMOOTH_GROUPS= Draw.Create(0)
993 CREATE_FGONS= Draw.Create(1)
994 CREATE_EDGES= Draw.Create(1)
995 SPLIT_OBJECTS= Draw.Create(0)
996 SPLIT_GROUPS= Draw.Create(0)
997 SPLIT_MATERIALS= Draw.Create(0)
998 CLAMP_SIZE= Draw.Create(10.0)
999 IMAGE_SEARCH= Draw.Create(1)
1000 POLYGROUPS= Draw.Create(0)
1001 KEEP_VERT_ORDER= Draw.Create(1)
1002 ROTATE_X90= Draw.Create(1)
1006 # Note, Works but not pretty, instead use a more complicated GUI
1010 ('Smooth Groups', CREATE_SMOOTH_GROUPS, 'Surround smooth groups by sharp edges'),\
1011 ('Create FGons', CREATE_FGONS, 'Import faces with more then 4 verts as fgons.'),\
1012 ('Lines', CREATE_EDGES, 'Import lines and faces with 2 verts as edges'),\
1013 'Separate objects from obj...',\
1014 ('Object', SPLIT_OBJECTS, 'Import OBJ Objects into Blender Objects'),\
1015 ('Group', SPLIT_GROUPS, 'Import OBJ Groups into Blender Objects'),\
1016 ('Material', SPLIT_MATERIALS, 'Import each material into a seperate mesh (Avoids > 16 per mesh error)'),\
1018 ('Keep Vert Order', KEEP_VERT_ORDER, 'Keep vert and face order, disables some other options.'),\
1019 ('Clamp Scale:', CLAMP_SIZE, 0.0, 1000.0, 'Clamp the size to this maximum (Zero to Disable)'),\
1020 ('Image Search', IMAGE_SEARCH, 'Search subdirs for any assosiated images (Warning, may be slow)'),\
1023 if not Draw.PupBlock('Import OBJ...', pup_block):
1026 if KEEP_VERT_ORDER.val:
1027 SPLIT_OBJECTS.val = False
1028 SPLIT_GROUPS.val = False
1029 SPLIT_MATERIALS.val = False
1034 # BEGIN ALTERNATIVE UI *******************
1043 GLOBALS['EVENT'] = EVENT_REDRAW
1044 #GLOBALS['MOUSE'] = Window.GetMouseCoords()
1045 GLOBALS['MOUSE'] = [i/2 for i in Window.GetScreenSize()]
1047 def obj_ui_set_event(e,v):
1048 GLOBALS['EVENT'] = e
1051 global SPLIT_OBJECTS, SPLIT_GROUPS, SPLIT_MATERIALS, KEEP_VERT_ORDER, POLYGROUPS
1052 if SPLIT_OBJECTS.val or SPLIT_GROUPS.val or SPLIT_MATERIALS.val:
1053 KEEP_VERT_ORDER.val = 0
1056 KEEP_VERT_ORDER.val = 1
1058 def do_vertorder(e,v):
1059 global SPLIT_OBJECTS, SPLIT_GROUPS, SPLIT_MATERIALS, KEEP_VERT_ORDER
1060 if KEEP_VERT_ORDER.val:
1061 SPLIT_OBJECTS.val = SPLIT_GROUPS.val = SPLIT_MATERIALS.val = 0
1063 if not (SPLIT_OBJECTS.val or SPLIT_GROUPS.val or SPLIT_MATERIALS.val):
1064 KEEP_VERT_ORDER.val = 1
1066 def do_polygroups(e,v):
1067 global SPLIT_OBJECTS, SPLIT_GROUPS, SPLIT_MATERIALS, KEEP_VERT_ORDER, POLYGROUPS
1069 SPLIT_OBJECTS.val = SPLIT_GROUPS.val = SPLIT_MATERIALS.val = 0
1073 print 'Trying to open web browser with documentation at this address...'
1078 webbrowser.open(url)
1080 print '...could not open a browser window.'
1083 ui_x, ui_y = GLOBALS['MOUSE']
1085 # Center based on overall pup size
1089 global CREATE_SMOOTH_GROUPS, CREATE_FGONS, CREATE_EDGES, SPLIT_OBJECTS, SPLIT_GROUPS, SPLIT_MATERIALS, CLAMP_SIZE, IMAGE_SEARCH, POLYGROUPS, KEEP_VERT_ORDER, ROTATE_X90
1091 Draw.Label('Import...', ui_x+9, ui_y+159, 220, 21)
1093 CREATE_SMOOTH_GROUPS = Draw.Toggle('Smooth Groups', EVENT_NONE, ui_x+9, ui_y+139, 110, 20, CREATE_SMOOTH_GROUPS.val, 'Surround smooth groups by sharp edges')
1094 CREATE_FGONS = Draw.Toggle('NGons as FGons', EVENT_NONE, ui_x+119, ui_y+139, 110, 20, CREATE_FGONS.val, 'Import faces with more then 4 verts as fgons')
1095 CREATE_EDGES = Draw.Toggle('Lines as Edges', EVENT_NONE, ui_x+229, ui_y+139, 110, 20, CREATE_EDGES.val, 'Import lines and faces with 2 verts as edges')
1098 Draw.Label('Separate objects by OBJ...', ui_x+9, ui_y+110, 220, 20)
1100 SPLIT_OBJECTS = Draw.Toggle('Object', EVENT_REDRAW, ui_x+9, ui_y+89, 55, 21, SPLIT_OBJECTS.val, 'Import OBJ Objects into Blender Objects', do_split)
1101 SPLIT_GROUPS = Draw.Toggle('Group', EVENT_REDRAW, ui_x+64, ui_y+89, 55, 21, SPLIT_GROUPS.val, 'Import OBJ Groups into Blender Objects', do_split)
1102 SPLIT_MATERIALS = Draw.Toggle('Material', EVENT_REDRAW, ui_x+119, ui_y+89, 60, 21, SPLIT_MATERIALS.val, 'Import each material into a seperate mesh (Avoids > 16 per mesh error)', do_split)
1105 # Only used for user feedback
1106 KEEP_VERT_ORDER = Draw.Toggle('Keep Vert Order', EVENT_REDRAW, ui_x+184, ui_y+89, 113, 21, KEEP_VERT_ORDER.val, 'Keep vert and face order, disables split options, enable for morph targets', do_vertorder)
1108 ROTATE_X90 = Draw.Toggle('-X90', EVENT_REDRAW, ui_x+302, ui_y+89, 38, 21, ROTATE_X90.val, 'Rotate X 90.')
1110 Draw.Label('Options...', ui_x+9, ui_y+60, 211, 20)
1111 CLAMP_SIZE = Draw.Number('Clamp Scale: ', EVENT_NONE, ui_x+9, ui_y+39, 130, 21, CLAMP_SIZE.val, 0.0, 1000.0, 'Clamp the size to this maximum (Zero to Disable)')
1112 POLYGROUPS = Draw.Toggle('Poly Groups', EVENT_REDRAW, ui_x+144, ui_y+39, 90, 21, POLYGROUPS.val, 'Import OBJ groups as vertex groups.', do_polygroups)
1113 IMAGE_SEARCH = Draw.Toggle('Image Search', EVENT_NONE, ui_x+239, ui_y+39, 100, 21, IMAGE_SEARCH.val, 'Search subdirs for any assosiated images (Warning, may be slow)')
1115 Draw.PushButton('Online Help', EVENT_REDRAW, ui_x+9, ui_y+9, 110, 21, 'Load the wiki page for this script', do_help)
1116 Draw.PushButton('Cancel', EVENT_EXIT, ui_x+119, ui_y+9, 110, 21, '', obj_ui_set_event)
1117 Draw.PushButton('Import', EVENT_IMPORT, ui_x+229, ui_y+9, 110, 21, 'Import with these settings', obj_ui_set_event)
1121 # hack so the toggle buttons redraw. this is not nice at all
1122 while GLOBALS['EVENT'] not in (EVENT_EXIT, EVENT_IMPORT):
1123 Draw.UIBlock(obj_ui, 0)
1125 if GLOBALS['EVENT'] != EVENT_IMPORT:
1128 # END ALTERNATIVE UI *********************
1136 Window.WaitCursor(1)
1138 if BATCH_LOAD: # load the dir
1140 files= [ f for f in os.listdir(filepath) if f.lower().endswith('.obj') ]
1142 Window.WaitCursor(0)
1143 Draw.PupMenu('Error%t|Could not open path ' + filepath)
1147 Window.WaitCursor(0)
1148 Draw.PupMenu('Error%t|No files at path ' + filepath)
1152 scn= bpy.data.scenes.new( stripExt(f) )
1155 load_obj(sys.join(filepath, f),\
1158 CREATE_SMOOTH_GROUPS.val,\
1162 SPLIT_MATERIALS.val,\
1172 CREATE_SMOOTH_GROUPS.val,\
1176 SPLIT_MATERIALS.val,\
1182 Window.WaitCursor(0)
1185 def load_obj_ui_batch(file):
1186 load_obj_ui(file, True)
1190 if __name__=='__main__' and not DEBUG:
1191 if os and Window.GetKeyQualifiers() & Window.Qual.SHIFT:
1192 Window.FileSelector(load_obj_ui_batch, 'Import OBJ Dir', '')
1194 Window.FileSelector(load_obj_ui, 'Import a Wavefront OBJ', '*.obj')
1196 # For testing compatibility
1203 print 'Searching for files'
1205 for dirpath, dirnames, filenames in os.walk(path):
1206 for filename in filenames:
1207 yield os.path.join(dirpath, filename)
1209 files = [f for f in fileList(DIR) if f.lower().endswith('.obj')]
1212 for i, obj_file in enumerate(files):
1214 print 'Importing', obj_file, '\nNUMBER', i, 'of', len(files)
1215 newScn= bpy.data.scenes.new(os.path.basename(obj_file))
1216 newScn.makeCurrent()
1217 load_obj(obj_file, False, IMAGE_SEARCH=0)
1219 print 'TOTAL TIME: %.6f' % (sys.time() - TIME)
1221 #load_obj('/test.obj')
1222 #load_obj('/fe/obj/mba1.obj')